Compare commits

...

3 Commits

Author SHA1 Message Date
Kim Kulling 8e4ee11bf3 Prototype new triangulation. 2021-01-13 22:43:46 +01:00
Kim Kulling 4bb2006325 add missing unittest. 2020-11-11 20:00:46 +01:00
Kim Kulling 456b54988a closes https://github.com/assimp/assimp/issues/1044: set default value in case of light intensity envelopes- 2020-11-11 19:38:42 +01:00
26 changed files with 2906 additions and 3441 deletions

View File

@ -123,20 +123,20 @@ const aiImporterDesc *FBXImporter::GetInfo() const {
// ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer *pImp) {
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
settings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
mSettings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
mSettings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
mSettings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
mSettings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
mSettings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
mSettings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
mSettings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
mSettings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
mSettings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
mSettings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
mSettings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
mSettings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
mSettings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
}
// ------------------------------------------------------------------------------------------------
@ -181,10 +181,10 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
Parser parser(tokens, is_binary);
// take the raw parse-tree and convert it to a FBX DOM
Document doc(parser, settings);
Document doc(parser, mSettings);
// convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
ConvertToAssimpScene(pScene, doc, mSettings.removeEmptyBones);
// size relative to cm
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();

View File

@ -70,27 +70,16 @@ typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>
class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> {
public:
FBXImporter();
virtual ~FBXImporter();
// --------------------
bool CanRead(const std::string &pFile,
IOSystem *pIOHandler,
bool checkSig) const;
~FBXImporter() override;
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
protected:
// --------------------
const aiImporterDesc *GetInfo() const;
// --------------------
void SetupProperties(const Importer *pImp);
// --------------------
void InternReadFile(const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler);
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
private:
FBX::ImportSettings settings;
FBX::ImportSettings mSettings;
}; // !class FBXImporter
} // end of namespace Assimp

View File

@ -48,12 +48,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <functional>
#include "FBXMeshGeometry.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXImportSettings.h"
#include "FBXDocumentUtil.h"
#include "FBXImportSettings.h"
#include "FBXImporter.h"
#include "FBXMeshGeometry.h"
namespace Assimp {
namespace FBX {
@ -61,17 +60,15 @@ namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Object(id, element, name)
, skin()
{
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
for(const Connection* con : conns) {
const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
if(sk) {
Geometry::Geometry(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
Object(id, element, name), skin() {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
for (const Connection *con : conns) {
const Skin *const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
if (sk) {
skin = sk;
}
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
const BlendShape *const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
if (bsp) {
blendShapes.push_back(bsp);
}
@ -79,48 +76,46 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
}
// ------------------------------------------------------------------------------------------------
Geometry::~Geometry()
{
Geometry::~Geometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
const std::vector<const BlendShape *> &Geometry::GetBlendShapes() const {
return blendShapes;
}
// ------------------------------------------------------------------------------------------------
const Skin* Geometry::DeformerSkin() const {
const Skin *Geometry::DeformerSkin() const {
return skin;
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element,name, doc)
{
const Scope* sc = element.Compound();
MeshGeometry::MeshGeometry(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
const Scope *sc = element.Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Mesh), no data scope found");
}
// must have Mesh elements:
const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element);
const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element);
const Element &Vertices = GetRequiredElement(*sc, "Vertices", &element);
const Element &PolygonVertexIndex = GetRequiredElement(*sc, "PolygonVertexIndex", &element);
// optional Mesh elements:
const ElementCollection& Layer = sc->GetCollection("Layer");
const ElementCollection &Layer = sc->GetCollection("Layer");
std::vector<aiVector3D> tempVerts;
ParseVectorDataArray(tempVerts,Vertices);
ParseVectorDataArray(tempVerts, Vertices);
if(tempVerts.empty()) {
if (tempVerts.empty()) {
FBXImporter::LogWarn("encountered mesh with no vertices");
}
std::vector<int> tempFaces;
ParseVectorDataArray(tempFaces,PolygonVertexIndex);
ParseVectorDataArray(tempFaces, PolygonVertexIndex);
if(tempFaces.empty()) {
if (tempFaces.empty()) {
FBXImporter::LogWarn("encountered mesh with no faces");
}
@ -128,7 +123,7 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
m_faces.reserve(tempFaces.size() / 3);
m_mapping_offsets.resize(tempVerts.size());
m_mapping_counts.resize(tempVerts.size(),0);
m_mapping_counts.resize(tempVerts.size(), 0);
m_mappings.resize(tempFaces.size());
const size_t vertex_count = tempVerts.size();
@ -136,10 +131,10 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
// generate output vertices, computing an adjacency table to
// preserve the mapping from fbx indices to *this* indexing.
unsigned int count = 0;
for(int index : tempFaces) {
for (int index : tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
if(static_cast<size_t>(absi) >= vertex_count) {
DOMError("polygon vertex index out of range",&PolygonVertexIndex);
if (static_cast<size_t>(absi) >= vertex_count) {
DOMError("polygon vertex index out of range", &PolygonVertexIndex);
}
m_vertices.push_back(tempVerts[absi]);
@ -162,7 +157,7 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
}
cursor = 0;
for(int index : tempFaces) {
for (int index : tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++;
}
@ -172,19 +167,18 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
// if settings.readAllLayers is false:
// * read only the layer with index 0, but warn about any further layers
for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
const TokenList& tokens = (*it).second->Tokens();
const TokenList &tokens = (*it).second->Tokens();
const char* err;
const char *err;
const int index = ParseTokenAsInt(*tokens[0], err);
if(err) {
DOMError(err,&element);
if (err) {
DOMError(err, &element);
}
if(doc.Settings().readAllLayers || index == 0) {
const Scope& layer = GetRequiredScope(*(*it).second);
if (doc.Settings().readAllLayers || index == 0) {
const Scope &layer = GetRequiredScope(*(*it).second);
ReadLayer(layer);
}
else {
} else {
FBXImporter::LogWarn("ignoring additional geometry layers");
}
}
@ -196,151 +190,142 @@ MeshGeometry::~MeshGeometry() {
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
const std::vector<aiVector3D> &MeshGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetNormals() const {
const std::vector<aiVector3D> &MeshGeometry::GetNormals() const {
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetTangents() const {
const std::vector<aiVector3D> &MeshGeometry::GetTangents() const {
return m_tangents;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const {
const std::vector<aiVector3D> &MeshGeometry::GetBinormals() const {
return m_binormals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const {
const std::vector<unsigned int> &MeshGeometry::GetFaceIndexCounts() const {
return m_faces;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const {
const std::vector<aiVector2D> &MeshGeometry::GetTextureCoords(unsigned int index) const {
static const std::vector<aiVector2D> empty;
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ];
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[index];
}
std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const {
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ];
std::string MeshGeometry::GetTextureCoordChannelName(unsigned int index) const {
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[index];
}
const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const {
const std::vector<aiColor4D> &MeshGeometry::GetVertexColors(unsigned int index) const {
static const std::vector<aiColor4D> empty;
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ];
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[index];
}
const MatIndexArray& MeshGeometry::GetMaterialIndices() const {
const MatIndexArray &MeshGeometry::GetMaterialIndices() const {
return m_materials;
}
// ------------------------------------------------------------------------------------------------
const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const {
if ( in_index >= m_mapping_counts.size() ) {
const unsigned int *MeshGeometry::ToOutputVertexIndex(unsigned int in_index, unsigned int &count) const {
if (in_index >= m_mapping_counts.size()) {
return nullptr;
}
ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
count = m_mapping_counts[ in_index ];
ai_assert(m_mapping_counts.size() == m_mapping_offsets.size());
count = m_mapping_counts[in_index];
ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
ai_assert(m_mapping_offsets[in_index] + count <= m_mappings.size());
return &m_mappings[ m_mapping_offsets[ in_index ] ];
return &m_mappings[m_mapping_offsets[in_index]];
}
// ------------------------------------------------------------------------------------------------
unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const {
ai_assert( in_index < m_vertices.size() );
unsigned int MeshGeometry::FaceForVertexIndex(unsigned int in_index) const {
ai_assert(in_index < m_vertices.size());
// in the current conversion pattern this will only be needed if
// weights are present, so no need to always pre-compute this table
if ( m_facesVertexStartIndices.empty() ) {
m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 );
if (m_facesVertexStartIndices.empty()) {
m_facesVertexStartIndices.resize(m_faces.size() + 1, 0);
std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 );
std::partial_sum(m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1);
m_facesVertexStartIndices.pop_back();
}
ai_assert( m_facesVertexStartIndices.size() == m_faces.size() );
ai_assert(m_facesVertexStartIndices.size() == m_faces.size());
const std::vector<unsigned int>::iterator it = std::upper_bound(
m_facesVertexStartIndices.begin(),
m_facesVertexStartIndices.end(),
in_index
);
m_facesVertexStartIndices.begin(),
m_facesVertexStartIndices.end(),
in_index);
return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) );
return static_cast<unsigned int>(std::distance(m_facesVertexStartIndices.begin(), it - 1));
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadLayer(const Scope& layer)
{
const ElementCollection& LayerElement = layer.GetCollection("LayerElement");
void MeshGeometry::ReadLayer(const Scope &layer) {
const ElementCollection &LayerElement = layer.GetCollection("LayerElement");
for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
const Scope& elayer = GetRequiredScope(*(*eit).second);
const Scope &elayer = GetRequiredScope(*(*eit).second);
ReadLayerElement(elayer);
}
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadLayerElement(const Scope& layerElement)
{
const Element& Type = GetRequiredElement(layerElement,"Type");
const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex");
void MeshGeometry::ReadLayerElement(const Scope &layerElement) {
const Element &Type = GetRequiredElement(layerElement, "Type");
const Element &TypedIndex = GetRequiredElement(layerElement, "TypedIndex");
const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0));
const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0));
const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0));
const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0));
const Scope& top = GetRequiredScope(element);
const Scope &top = GetRequiredScope(element);
const ElementCollection candidates = top.GetCollection(type);
for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) {
const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0));
if(index == typedIndex) {
ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second));
const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second, 0));
if (index == typedIndex) {
ReadVertexData(type, typedIndex, GetRequiredScope(*(*it).second));
return;
}
}
FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ")
<< type << ", index: " << typedIndex);
<< type << ", index: " << typedIndex);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source)
{
const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source,"MappingInformationType"),0)
);
void MeshGeometry::ReadVertexData(const std::string &type, int index, const Scope &source) {
const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source, "MappingInformationType"), 0));
const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source,"ReferenceInformationType"),0)
);
const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source, "ReferenceInformationType"), 0));
if (type == "LayerElementUV") {
if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
if (index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ")
<< index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" );
<< index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")");
return;
}
const Element* Name = source["Name"];
const Element *Name = source["Name"];
m_uvNames[index] = "";
if(Name) {
m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
if (Name) {
m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name, 0));
}
ReadVertexDataUV(m_uvs[index],source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementMaterial") {
ReadVertexDataUV(m_uvs[index], source,
MappingInformationType,
ReferenceInformationType);
} else if (type == "LayerElementMaterial") {
if (m_materials.size() > 0) {
FBXImporter::LogError("ignoring additional material layer");
return;
@ -348,10 +333,9 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
std::vector<int> temp_materials;
ReadVertexDataMaterials(temp_materials,source,
MappingInformationType,
ReferenceInformationType
);
ReadVertexDataMaterials(temp_materials, source,
MappingInformationType,
ReferenceInformationType);
// sometimes, there will be only negative entries. Drop the material
// layer in such a case (I guess it means a default material should
@ -359,58 +343,50 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
// avoids losing the material if there are more material layers
// coming of which at least one contains actual data (did observe
// that with one test file).
const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; });
if(count_neg == temp_materials.size()) {
const size_t count_neg = std::count_if(temp_materials.begin(), temp_materials.end(), [](int n) { return n < 0; });
if (count_neg == temp_materials.size()) {
FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)");
return;
}
std::swap(temp_materials, m_materials);
}
else if (type == "LayerElementNormal") {
} else if (type == "LayerElementNormal") {
if (m_normals.size() > 0) {
FBXImporter::LogError("ignoring additional normal layer");
return;
}
ReadVertexDataNormals(m_normals,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementTangent") {
ReadVertexDataNormals(m_normals, source,
MappingInformationType,
ReferenceInformationType);
} else if (type == "LayerElementTangent") {
if (m_tangents.size() > 0) {
FBXImporter::LogError("ignoring additional tangent layer");
return;
}
ReadVertexDataTangents(m_tangents,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementBinormal") {
ReadVertexDataTangents(m_tangents, source,
MappingInformationType,
ReferenceInformationType);
} else if (type == "LayerElementBinormal") {
if (m_binormals.size() > 0) {
FBXImporter::LogError("ignoring additional binormal layer");
return;
}
ReadVertexDataBinormals(m_binormals,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementColor") {
if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
ReadVertexDataBinormals(m_binormals, source,
MappingInformationType,
ReferenceInformationType);
} else if (type == "LayerElementColor") {
if (index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ")
<< index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" );
<< index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")");
return;
}
ReadVertexDataColors(m_colors[index],source,
MappingInformationType,
ReferenceInformationType
);
ReadVertexDataColors(m_colors[index], source,
MappingInformationType,
ReferenceInformationType);
}
}
@ -419,21 +395,20 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
// output is in polygon vertex order. This logic is used for reading normals, UVs, colors,
// tangents ..
template <typename T>
void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType,
const char* dataElementName,
const char* indexDataElementName,
size_t vertex_count,
const std::vector<unsigned int>& mapping_counts,
const std::vector<unsigned int>& mapping_offsets,
const std::vector<unsigned int>& mappings)
{
void ResolveVertexDataArray(std::vector<T> &data_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType,
const char *dataElementName,
const char *indexDataElementName,
size_t vertex_count,
const std::vector<unsigned int> &mapping_counts,
const std::vector<unsigned int> &mapping_offsets,
const std::vector<unsigned int> &mappings) {
bool isDirect = ReferenceInformationType == "Direct";
bool isIndexToDirect = ReferenceInformationType == "IndexToDirect";
// fall-back to direct data if there is no index data element
if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) {
if (isIndexToDirect && !HasElement(source, indexDataElementName)) {
isDirect = true;
isIndexToDirect = false;
}
@ -461,13 +436,12 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
data_out[mappings[j]] = tempData[i];
}
}
}
else if (MappingInformationType == "ByVertice" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
} else if (MappingInformationType == "ByVertice" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
std::vector<int> uvIndices;
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
ParseVectorDataArray(uvIndices, GetRequiredElement(source, indexDataElementName));
if (uvIndices.size() != vertex_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByVertice mapping: ")
@ -481,32 +455,29 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
for (unsigned int j = istart; j < iend; ++j) {
if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) {
DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) {
DOMError("index out of range", &GetRequiredElement(source, indexDataElementName));
}
data_out[mappings[j]] = tempData[uvIndices[i]];
data_out[mappings[j]] = tempData[uvIndices[i]];
}
}
}
else if (MappingInformationType == "ByPolygonVertex" && isDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
} else if (MappingInformationType == "ByPolygonVertex" && isDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
if (tempData.size() != vertex_count) {
if (tempData.size() != vertex_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
<< tempData.size() << ", expected " << vertex_count
);
<< tempData.size() << ", expected " << vertex_count);
return;
}
data_out.swap(tempData);
}
else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
data_out.swap(tempData);
} else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
std::vector<int> uvIndices;
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
ParseVectorDataArray(uvIndices, GetRequiredElement(source, indexDataElementName));
if (uvIndices.size() != vertex_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygonVertex mapping: ")
@ -518,120 +489,111 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
const T empty;
unsigned int next = 0;
for(int i : uvIndices) {
if ( -1 == i ) {
data_out[ next++ ] = empty;
for (int i : uvIndices) {
if (-1 == i) {
data_out[next++] = empty;
continue;
}
if (static_cast<size_t>(i) >= tempData.size()) {
DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
DOMError("index out of range", &GetRequiredElement(source, indexDataElementName));
}
data_out[next++] = tempData[i];
data_out[next++] = tempData[i];
}
}
else {
} else {
FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ")
<< MappingInformationType << "," << ReferenceInformationType);
<< MappingInformationType << "," << ReferenceInformationType);
}
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
"Normals",
"NormalsIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D> &normals_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
ResolveVertexDataArray(normals_out, source, MappingInformationType, ReferenceInformationType,
"Normals",
"NormalsIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
"UV",
"UVIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D> &uv_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
ResolveVertexDataArray(uv_out, source, MappingInformationType, ReferenceInformationType,
"UV",
"UVIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
"Colors",
"ColorIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D> &colors_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
ResolveVertexDataArray(colors_out, source, MappingInformationType, ReferenceInformationType,
"Colors",
"ColorIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
static const char *TangentIndexToken = "TangentIndex";
static const char *TangentsIndexToken = "TangentsIndex";
void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken;
ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D> &tangents_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
const char *str = source.Elements().count("Tangents") > 0 ? "Tangents" : "Tangent";
const char *strIdx = source.Elements().count("Tangents") > 0 ? TangentsIndexToken : TangentIndexToken;
ResolveVertexDataArray(tangents_out, source, MappingInformationType, ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
static const std::string BinormalIndexToken = "BinormalIndex";
static const std::string BinormalsIndexToken = "BinormalsIndex";
void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str();
ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D> &binormals_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
const char *str = source.Elements().count("Binormals") > 0 ? "Binormals" : "Binormal";
const char *strIdx = source.Elements().count("Binormals") > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str();
ResolveVertexDataArray(binormals_out, source, MappingInformationType, ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
void MeshGeometry::ReadVertexDataMaterials(std::vector<int> &materials_out, const Scope &source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType) {
const size_t face_count = m_faces.size();
if( 0 == face_count )
{
if (0 == face_count) {
return;
}
// materials are handled separately. First of all, they are assigned per-face
// and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
// has a slightly different meaning for materials.
ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials"));
ParseVectorDataArray(materials_out, GetRequiredElement(source, "Materials"));
if (MappingInformationType == "AllSame") {
// easy - same material for all faces
@ -648,27 +610,26 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
} else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
materials_out.resize(face_count);
if(materials_out.size() != face_count) {
if (materials_out.size() != face_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
<< materials_out.size() << ", expected " << face_count
);
<< materials_out.size() << ", expected " << face_count);
return;
}
} else {
FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
<< MappingInformationType << "," << ReferenceInformationType);
<< MappingInformationType << "," << ReferenceInformationType);
}
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element, name, doc) {
ShapeGeometry::ShapeGeometry(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
const Scope *sc = element.Compound();
if (nullptr == sc) {
DOMError("failed to read Geometry object (class: Shape), no data scope found");
}
const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
const Element &Indexes = GetRequiredElement(*sc, "Indexes", &element);
const Element &Normals = GetRequiredElement(*sc, "Normals", &element);
const Element &Vertices = GetRequiredElement(*sc, "Vertices", &element);
ParseVectorDataArray(m_indices, Indexes);
ParseVectorDataArray(m_vertices, Vertices);
ParseVectorDataArray(m_normals, Normals);
@ -679,27 +640,26 @@ ShapeGeometry::~ShapeGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& ShapeGeometry::GetVertices() const {
const std::vector<aiVector3D> &ShapeGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& ShapeGeometry::GetNormals() const {
const std::vector<aiVector3D> &ShapeGeometry::GetNormals() const {
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int>& ShapeGeometry::GetIndices() const {
const std::vector<unsigned int> &ShapeGeometry::GetIndices() const {
return m_indices;
}
// ------------------------------------------------------------------------------------------------
LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element, name, doc)
{
const Scope* sc = element.Compound();
LineGeometry::LineGeometry(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
const Scope *sc = element.Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Line), no data scope found");
}
const Element& Points = GetRequiredElement(*sc, "Points", &element);
const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element);
const Element &Points = GetRequiredElement(*sc, "Points", &element);
const Element &PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element);
ParseVectorDataArray(m_vertices, Points);
ParseVectorDataArray(m_indices, PointsIndex);
}
@ -709,14 +669,13 @@ LineGeometry::~LineGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& LineGeometry::GetVertices() const {
const std::vector<aiVector3D> &LineGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<int>& LineGeometry::GetIndices() const {
const std::vector<int> &LineGeometry::GetIndices() const {
return m_indices;
}
} // !FBX
} // !Assimp
} // namespace FBX
} // namespace Assimp
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -55,20 +55,19 @@ namespace Assimp {
namespace IFC {
// ------------------------------------------------------------------------------------------------
void TempOpening::Transform(const IfcMatrix4& mat) {
if(profileMesh) {
void TempOpening::Transform(const IfcMatrix4 &mat) {
if (profileMesh) {
profileMesh->Transform(mat);
}
if(profileMesh2D) {
if (profileMesh2D) {
profileMesh2D->Transform(mat);
}
extrusionDir *= IfcMatrix3(mat);
}
// ------------------------------------------------------------------------------------------------
aiMesh* TempMesh::ToMesh()
{
ai_assert(mVerts.size() == std::accumulate(mVertcnt.begin(),mVertcnt.end(),size_t(0)));
aiMesh *TempMesh::ToMesh() {
ai_assert(mVerts.size() == std::accumulate(mVertcnt.begin(), mVertcnt.end(), size_t(0)));
if (mVerts.empty()) {
return nullptr;
@ -79,14 +78,14 @@ aiMesh* TempMesh::ToMesh()
// copy vertices
mesh->mNumVertices = static_cast<unsigned int>(mVerts.size());
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
std::copy(mVerts.begin(),mVerts.end(),mesh->mVertices);
std::copy(mVerts.begin(), mVerts.end(), mesh->mVertices);
// and build up faces
mesh->mNumFaces = static_cast<unsigned int>(mVertcnt.size());
mesh->mFaces = new aiFace[mesh->mNumFaces];
for(unsigned int i = 0,n=0, acc = 0; i < mesh->mNumFaces; ++n) {
aiFace& f = mesh->mFaces[i];
for (unsigned int i = 0, n = 0, acc = 0; i < mesh->mNumFaces; ++n) {
aiFace &f = mesh->mFaces[i];
if (!mVertcnt[n]) {
--mesh->mNumFaces;
continue;
@ -94,7 +93,7 @@ aiMesh* TempMesh::ToMesh()
f.mNumIndices = mVertcnt[n];
f.mIndices = new unsigned int[f.mNumIndices];
for(unsigned int a = 0; a < f.mNumIndices; ++a) {
for (unsigned int a = 0; a < f.mNumIndices; ++a) {
f.mIndices[a] = acc++;
}
@ -105,36 +104,31 @@ aiMesh* TempMesh::ToMesh()
}
// ------------------------------------------------------------------------------------------------
void TempMesh::Clear()
{
void TempMesh::Clear() {
mVerts.clear();
mVertcnt.clear();
}
// ------------------------------------------------------------------------------------------------
void TempMesh::Transform(const IfcMatrix4& mat)
{
for(IfcVector3& v : mVerts) {
void TempMesh::Transform(const IfcMatrix4 &mat) {
for (IfcVector3 &v : mVerts) {
v *= mat;
}
}
// ------------------------------------------------------------------------------
IfcVector3 TempMesh::Center() const
{
return (mVerts.size() == 0) ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(mVerts.begin(),mVerts.end(),IfcVector3()) / static_cast<IfcFloat>(mVerts.size()));
IfcVector3 TempMesh::Center() const {
return (mVerts.size() == 0) ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(mVerts.begin(), mVerts.end(), IfcVector3()) / static_cast<IfcFloat>(mVerts.size()));
}
// ------------------------------------------------------------------------------------------------
void TempMesh::Append(const TempMesh& other)
{
mVerts.insert(mVerts.end(),other.mVerts.begin(),other.mVerts.end());
mVertcnt.insert(mVertcnt.end(),other.mVertcnt.begin(),other.mVertcnt.end());
void TempMesh::Append(const TempMesh &other) {
mVerts.insert(mVerts.end(), other.mVerts.begin(), other.mVerts.end());
mVertcnt.insert(mVertcnt.end(), other.mVertcnt.begin(), other.mVertcnt.end());
}
// ------------------------------------------------------------------------------------------------
void TempMesh::RemoveDegenerates()
{
void TempMesh::RemoveDegenerates() {
// The strategy is simple: walk the mesh and compute normals using
// Newell's algorithm. The length of the normals gives the area
// of the polygons, which is close to zero for lines.
@ -161,52 +155,48 @@ void TempMesh::RemoveDegenerates()
++it;
}
if(drop) {
if (drop) {
IFCImporter::LogVerboseDebug("removing degenerate faces");
}
}
// ------------------------------------------------------------------------------------------------
IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize)
{
std::vector<IfcFloat> temp((cnt+2)*3);
for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs )
{
const IfcVector3& v = vtcs[vofs];
IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3 *vtcs, size_t cnt, bool normalize) {
const size_t Capa = cnt + 2;
std::vector<IfcFloat> temp((Capa)*3);
for (size_t vofs = 0, i = 0; vofs < cnt; ++vofs) {
const IfcVector3 &v = vtcs[vofs];
temp[i++] = v.x;
temp[i++] = v.y;
temp[i++] = v.z;
}
IfcVector3 nor;
NewellNormal<3, 3, 3>(nor, static_cast<int>(cnt), &temp[0], &temp[1], &temp[2]);
NewellNormal<3, 3, 3>(nor, static_cast<int>(cnt), &temp[0], &temp[1], &temp[2], Capa);
return normalize ? nor.Normalize() : nor;
}
// ------------------------------------------------------------------------------------------------
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
bool normalize,
size_t ofs) const
{
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &normals, bool normalize, size_t ofs) const {
size_t max_vcount = 0;
std::vector<unsigned int>::const_iterator begin = mVertcnt.begin()+ofs, end = mVertcnt.end(), iit;
for(iit = begin; iit != end; ++iit) {
max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
std::vector<unsigned int>::const_iterator begin = mVertcnt.begin() + ofs, end = mVertcnt.end(), iit;
for (iit = begin; iit != end; ++iit) {
max_vcount = std::max(max_vcount, static_cast<size_t>(*iit));
}
std::vector<IfcFloat> temp((max_vcount+2)*4);
normals.reserve( normals.size() + mVertcnt.size()-ofs );
const size_t Capa = max_vcount + 2;
std::vector<IfcFloat> temp(Capa * 4);
normals.reserve(normals.size() + mVertcnt.size() - ofs);
// `NewellNormal()` currently has a relatively strange interface and need to
// re-structure things a bit to meet them.
size_t vidx = std::accumulate(mVertcnt.begin(),begin,0);
for(iit = begin; iit != end; vidx += *iit++) {
size_t vidx = std::accumulate(mVertcnt.begin(), begin, 0);
for (iit = begin; iit != end; vidx += *iit++) {
if (!*iit) {
normals.push_back(IfcVector3());
normals.emplace_back();
continue;
}
for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
const IfcVector3& v = mVerts[vidx+vofs];
for (size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
const IfcVector3 &v = mVerts[vidx + vofs];
temp[cnt++] = v.x;
temp[cnt++] = v.y;
temp[cnt++] = v.z;
@ -216,12 +206,12 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
++cnt;
}
normals.push_back(IfcVector3());
NewellNormal<4,4,4>(normals.back(),*iit,&temp[0],&temp[1],&temp[2]);
normals.emplace_back();
NewellNormal<4, 4, 4>(normals.back(), *iit, &temp[0], &temp[1], &temp[2], Capa);
}
if(normalize) {
for(IfcVector3& n : normals) {
if (normalize) {
for (IfcVector3 &n : normals) {
n.Normalize();
}
}
@ -229,62 +219,55 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
// ------------------------------------------------------------------------------------------------
// Compute the normal of the last polygon in the given mesh
IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const
{
IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const {
return ComputePolygonNormal(&mVerts[mVerts.size() - mVertcnt.back()], mVertcnt.back(), normalize);
}
struct CompareVector
{
bool operator () (const IfcVector3& a, const IfcVector3& b) const
{
struct CompareVector {
bool operator()(const IfcVector3 &a, const IfcVector3 &b) const {
IfcVector3 d = a - b;
IfcFloat eps = 1e-6;
return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
}
};
struct FindVector
{
struct FindVector {
IfcVector3 v;
FindVector(const IfcVector3& p) : v(p) { }
bool operator () (const IfcVector3& p) { return FuzzyVectorCompare(1e-6)(p, v); }
FindVector(const IfcVector3 &p) :
v(p) {}
bool operator()(const IfcVector3 &p) { return FuzzyVectorCompare(1e-6)(p, v); }
};
// ------------------------------------------------------------------------------------------------
void TempMesh::FixupFaceOrientation()
{
void TempMesh::FixupFaceOrientation() {
const IfcVector3 vavg = Center();
// create a list of start indices for all faces to allow random access to faces
std::vector<size_t> faceStartIndices(mVertcnt.size());
for( size_t i = 0, a = 0; a < mVertcnt.size(); i += mVertcnt[a], ++a )
for (size_t i = 0, a = 0; a < mVertcnt.size(); i += mVertcnt[a], ++a)
faceStartIndices[a] = i;
// list all faces on a vertex
std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
for( size_t a = 0; a < mVertcnt.size(); ++a )
{
for( size_t b = 0; b < mVertcnt[a]; ++b )
for (size_t a = 0; a < mVertcnt.size(); ++a) {
for (size_t b = 0; b < mVertcnt[a]; ++b)
facesByVertex[mVerts[faceStartIndices[a] + b]].push_back(a);
}
// determine neighbourhood for all polys
std::vector<size_t> neighbour(mVerts.size(), SIZE_MAX);
std::vector<size_t> tempIntersect(10);
for( size_t a = 0; a < mVertcnt.size(); ++a )
{
for( size_t b = 0; b < mVertcnt[a]; ++b )
{
for (size_t a = 0; a < mVertcnt.size(); ++a) {
for (size_t b = 0; b < mVertcnt[a]; ++b) {
size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % mVertcnt[a];
const std::vector<size_t>& facesOnB = facesByVertex[mVerts[ib]];
const std::vector<size_t>& facesOnNB = facesByVertex[mVerts[nib]];
const std::vector<size_t> &facesOnB = facesByVertex[mVerts[ib]];
const std::vector<size_t> &facesOnNB = facesByVertex[mVerts[nib]];
// there should be exactly one or two faces which appear in both lists. Our face and the other side
std::vector<size_t>::iterator sectstart = tempIntersect.begin();
std::vector<size_t>::iterator sectend = std::set_intersection(
facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
if( std::distance(sectstart, sectend) != 2 )
if (std::distance(sectstart, sectend) != 2)
continue;
if( *sectstart == a )
if (*sectstart == a)
++sectstart;
neighbour[ib] = *sectstart;
}
@ -294,30 +277,31 @@ void TempMesh::FixupFaceOrientation()
// facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring
// faces to have the same winding until all faces have been tested.
std::vector<bool> faceDone(mVertcnt.size(), false);
while( std::count(faceDone.begin(), faceDone.end(), false) != 0 )
{
while (std::count(faceDone.begin(), faceDone.end(), false) != 0) {
// find the farthest of the remaining faces
size_t farthestIndex = SIZE_MAX;
IfcFloat farthestDistance = -1.0;
for( size_t a = 0; a < mVertcnt.size(); ++a )
{
if( faceDone[a] )
for (size_t a = 0; a < mVertcnt.size(); ++a) {
if (faceDone[a])
continue;
IfcVector3 faceCenter = std::accumulate(mVerts.begin() + faceStartIndices[a],
mVerts.begin() + faceStartIndices[a] + mVertcnt[a], IfcVector3(0.0)) / IfcFloat(mVertcnt[a]);
mVerts.begin() + faceStartIndices[a] + mVertcnt[a], IfcVector3(0.0)) /
IfcFloat(mVertcnt[a]);
IfcFloat dst = (faceCenter - vavg).SquareLength();
if( dst > farthestDistance ) { farthestDistance = dst; farthestIndex = a; }
if (dst > farthestDistance) {
farthestDistance = dst;
farthestIndex = a;
}
}
// calculate its normal and reverse the poly if its facing towards the mesh center
IfcVector3 farthestNormal = ComputePolygonNormal(mVerts.data() + faceStartIndices[farthestIndex], mVertcnt[farthestIndex]);
IfcVector3 farthestCenter = std::accumulate(mVerts.begin() + faceStartIndices[farthestIndex],
mVerts.begin() + faceStartIndices[farthestIndex] + mVertcnt[farthestIndex], IfcVector3(0.0))
/ IfcFloat(mVertcnt[farthestIndex]);
mVerts.begin() + faceStartIndices[farthestIndex] + mVertcnt[farthestIndex], IfcVector3(0.0)) /
IfcFloat(mVertcnt[farthestIndex]);
// We accept a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in
// the file.
if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 )
{
if ((farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4) {
size_t fsi = faceStartIndices[farthestIndex], fvc = mVertcnt[farthestIndex];
std::reverse(mVerts.begin() + fsi, mVerts.begin() + fsi + fvc);
std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
@ -326,7 +310,7 @@ void TempMesh::FixupFaceOrientation()
// Before: points A - B - C - D with edge neighbour p - q - r - s
// After: points D - C - B - A, reversed neighbours are s - r - q - p, but the should be
// r q p s
for( size_t a = 0; a < fvc - 1; ++a )
for (size_t a = 0; a < fvc - 1; ++a)
std::swap(neighbour[fsi + a], neighbour[fsi + a + 1]);
}
faceDone[farthestIndex] = true;
@ -334,21 +318,19 @@ void TempMesh::FixupFaceOrientation()
todo.push_back(farthestIndex);
// go over its neighbour faces recursively and adapt their winding order to match the farthest face
while( !todo.empty() )
{
while (!todo.empty()) {
size_t tdf = todo.back();
size_t vsi = faceStartIndices[tdf], vc = mVertcnt[tdf];
todo.pop_back();
// check its neighbours
for( size_t a = 0; a < vc; ++a )
{
for (size_t a = 0; a < vc; ++a) {
// ignore neighbours if we already checked them
size_t nbi = neighbour[vsi + a];
if( nbi == SIZE_MAX || faceDone[nbi] )
if (nbi == SIZE_MAX || faceDone[nbi])
continue;
const IfcVector3& vp = mVerts[vsi + a];
const IfcVector3 &vp = mVerts[vsi + a];
size_t nbvsi = faceStartIndices[nbi], nbvc = mVertcnt[nbi];
std::vector<IfcVector3>::iterator it = std::find_if(mVerts.begin() + nbvsi, mVerts.begin() + nbvsi + nbvc, FindVector(vp));
ai_assert(it != mVerts.begin() + nbvsi + nbvc);
@ -358,8 +340,7 @@ void TempMesh::FixupFaceOrientation()
// to reverse the neighbour
nb_vidx = (nb_vidx + 1) % nbvc;
size_t oursideidx = (a + 1) % vc;
if( FuzzyVectorCompare(1e-6)(mVerts[vsi + oursideidx], mVerts[nbvsi + nb_vidx]) )
{
if (FuzzyVectorCompare(1e-6)(mVerts[vsi + oursideidx], mVerts[nbvsi + nb_vidx])) {
std::reverse(mVerts.begin() + nbvsi, mVerts.begin() + nbvsi + nbvc);
std::reverse(neighbour.begin() + nbvsi, neighbour.begin() + nbvsi + nbvc);
for (size_t aa = 0; aa < nbvc - 1; ++aa) {
@ -381,17 +362,16 @@ void TempMesh::FixupFaceOrientation()
void TempMesh::RemoveAdjacentDuplicates() {
bool drop = false;
std::vector<IfcVector3>::iterator base = mVerts.begin();
for(unsigned int& cnt : mVertcnt) {
if (cnt < 2){
for (unsigned int &cnt : mVertcnt) {
if (cnt < 2) {
base += cnt;
continue;
}
IfcVector3 vmin,vmax;
ArrayBounds(&*base, cnt ,vmin,vmax);
IfcVector3 vmin, vmax;
ArrayBounds(&*base, cnt, vmin, vmax);
const IfcFloat epsilon = (vmax-vmin).SquareLength() / static_cast<IfcFloat>(1e9);
const IfcFloat epsilon = (vmax - vmin).SquareLength() / static_cast<IfcFloat>(1e9);
//const IfcFloat dotepsilon = 1e-9;
//// look for vertices that lie directly on the line between their predecessor and their
@ -419,153 +399,127 @@ void TempMesh::RemoveAdjacentDuplicates() {
// drop any identical, adjacent vertices. this pass will collect the dropouts
// of the previous pass as a side-effect.
FuzzyVectorCompare fz(epsilon);
std::vector<IfcVector3>::iterator end = base+cnt, e = std::unique( base, end, fz );
std::vector<IfcVector3>::iterator end = base + cnt, e = std::unique(base, end, fz);
if (e != end) {
cnt -= static_cast<unsigned int>(std::distance(e, end));
mVerts.erase(e,end);
drop = true;
mVerts.erase(e, end);
drop = true;
}
// check front and back vertices for this polygon
if (cnt > 1 && fz(*base,*(base+cnt-1))) {
mVerts.erase(base+ --cnt);
drop = true;
if (cnt > 1 && fz(*base, *(base + cnt - 1))) {
mVerts.erase(base + --cnt);
drop = true;
}
// removing adjacent duplicates shouldn't erase everything :-)
ai_assert(cnt>0);
ai_assert(cnt > 0);
base += cnt;
}
if(drop) {
if (drop) {
IFCImporter::LogVerboseDebug("removing duplicate vertices");
}
}
// ------------------------------------------------------------------------------------------------
void TempMesh::Swap(TempMesh& other)
{
void TempMesh::Swap(TempMesh &other) {
mVertcnt.swap(other.mVertcnt);
mVerts.swap(other.mVerts);
}
// ------------------------------------------------------------------------------------------------
bool IsTrue(const ::Assimp::STEP::EXPRESS::BOOLEAN& in)
{
bool IsTrue(const ::Assimp::STEP::EXPRESS::BOOLEAN &in) {
return (std::string)in == "TRUE" || (std::string)in == "T";
}
// ------------------------------------------------------------------------------------------------
IfcFloat ConvertSIPrefix(const std::string& prefix)
{
IfcFloat ConvertSIPrefix(const std::string &prefix) {
if (prefix == "EXA") {
return 1e18f;
}
else if (prefix == "PETA") {
} else if (prefix == "PETA") {
return 1e15f;
}
else if (prefix == "TERA") {
} else if (prefix == "TERA") {
return 1e12f;
}
else if (prefix == "GIGA") {
} else if (prefix == "GIGA") {
return 1e9f;
}
else if (prefix == "MEGA") {
} else if (prefix == "MEGA") {
return 1e6f;
}
else if (prefix == "KILO") {
} else if (prefix == "KILO") {
return 1e3f;
}
else if (prefix == "HECTO") {
} else if (prefix == "HECTO") {
return 1e2f;
}
else if (prefix == "DECA") {
} else if (prefix == "DECA") {
return 1e-0f;
}
else if (prefix == "DECI") {
} else if (prefix == "DECI") {
return 1e-1f;
}
else if (prefix == "CENTI") {
} else if (prefix == "CENTI") {
return 1e-2f;
}
else if (prefix == "MILLI") {
} else if (prefix == "MILLI") {
return 1e-3f;
}
else if (prefix == "MICRO") {
} else if (prefix == "MICRO") {
return 1e-6f;
}
else if (prefix == "NANO") {
} else if (prefix == "NANO") {
return 1e-9f;
}
else if (prefix == "PICO") {
} else if (prefix == "PICO") {
return 1e-12f;
}
else if (prefix == "FEMTO") {
} else if (prefix == "FEMTO") {
return 1e-15f;
}
else if (prefix == "ATTO") {
} else if (prefix == "ATTO") {
return 1e-18f;
}
else {
} else {
IFCImporter::LogError("Unrecognized SI prefix: " + prefix);
return 1;
}
}
// ------------------------------------------------------------------------------------------------
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in)
{
out.r = static_cast<float>( in.Red );
out.g = static_cast<float>( in.Green );
out.b = static_cast<float>( in.Blue );
out.a = static_cast<float>( 1.f );
void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourRgb &in) {
out.r = static_cast<float>(in.Red);
out.g = static_cast<float>(in.Green);
out.b = static_cast<float>(in.Blue);
out.a = static_cast<float>(1.f);
}
// ------------------------------------------------------------------------------------------------
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base)
{
if (const ::Assimp::STEP::EXPRESS::REAL* const r = in.ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourOrFactor &in, ConversionData &conv, const aiColor4D *base) {
if (const ::Assimp::STEP::EXPRESS::REAL *const r = in.ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
out.r = out.g = out.b = static_cast<float>(*r);
if(base) {
out.r *= static_cast<float>( base->r );
out.g *= static_cast<float>( base->g );
out.b *= static_cast<float>( base->b );
out.a = static_cast<float>( base->a );
}
else out.a = 1.0;
}
else if (const Schema_2x3::IfcColourRgb* const rgb = in.ResolveSelectPtr<Schema_2x3::IfcColourRgb>(conv.db)) {
ConvertColor(out,*rgb);
}
else {
if (base) {
out.r *= static_cast<float>(base->r);
out.g *= static_cast<float>(base->g);
out.b *= static_cast<float>(base->b);
out.a = static_cast<float>(base->a);
} else
out.a = 1.0;
} else if (const Schema_2x3::IfcColourRgb *const rgb = in.ResolveSelectPtr<Schema_2x3::IfcColourRgb>(conv.db)) {
ConvertColor(out, *rgb);
} else {
IFCImporter::LogWarn("skipping unknown IfcColourOrFactor entity");
}
}
// ------------------------------------------------------------------------------------------------
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in)
{
void ConvertCartesianPoint(IfcVector3 &out, const Schema_2x3::IfcCartesianPoint &in) {
out = IfcVector3();
for(size_t i = 0; i < in.Coordinates.size(); ++i) {
for (size_t i = 0; i < in.Coordinates.size(); ++i) {
out[static_cast<unsigned int>(i)] = in.Coordinates[i];
}
}
// ------------------------------------------------------------------------------------------------
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in)
{
ConvertDirection(out,in.Orientation);
void ConvertVector(IfcVector3 &out, const Schema_2x3::IfcVector &in) {
ConvertDirection(out, in.Orientation);
out *= in.Magnitude;
}
// ------------------------------------------------------------------------------------------------
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in)
{
void ConvertDirection(IfcVector3 &out, const Schema_2x3::IfcDirection &in) {
out = IfcVector3();
for(size_t i = 0; i < in.DirectionRatios.size(); ++i) {
for (size_t i = 0; i < in.DirectionRatios.size(); ++i) {
out[static_cast<unsigned int>(i)] = in.DirectionRatios[i];
}
const IfcFloat len = out.Length();
if (len<1e-6) {
if (len < 1e-6) {
IFCImporter::LogWarn("direction vector magnitude too small, normalization would result in a division by zero");
return;
}
@ -573,8 +527,7 @@ void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in)
}
// ------------------------------------------------------------------------------------------------
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z)
{
void AssignMatrixAxes(IfcMatrix4 &out, const IfcVector3 &x, const IfcVector3 &y, const IfcVector3 &z) {
out.a1 = x.x;
out.b1 = x.y;
out.c1 = x.z;
@ -589,116 +542,105 @@ void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y,
}
// ------------------------------------------------------------------------------------------------
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in)
{
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement3D &in) {
IfcVector3 loc;
ConvertCartesianPoint(loc,in.Location);
ConvertCartesianPoint(loc, in.Location);
IfcVector3 z(0.f,0.f,1.f),r(1.f,0.f,0.f),x;
IfcVector3 z(0.f, 0.f, 1.f), r(1.f, 0.f, 0.f), x;
if (in.Axis) {
ConvertDirection(z,*in.Axis.Get());
ConvertDirection(z, *in.Axis.Get());
}
if (in.RefDirection) {
ConvertDirection(r,*in.RefDirection.Get());
ConvertDirection(r, *in.RefDirection.Get());
}
IfcVector3 v = r.Normalize();
IfcVector3 tmpx = z * (v*z);
IfcVector3 tmpx = z * (v * z);
x = (v-tmpx).Normalize();
IfcVector3 y = (z^x);
x = (v - tmpx).Normalize();
IfcVector3 y = (z ^ x);
IfcMatrix4::Translation(loc,out);
AssignMatrixAxes(out,x,y,z);
IfcMatrix4::Translation(loc, out);
AssignMatrixAxes(out, x, y, z);
}
// ------------------------------------------------------------------------------------------------
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in)
{
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement2D &in) {
IfcVector3 loc;
ConvertCartesianPoint(loc,in.Location);
ConvertCartesianPoint(loc, in.Location);
IfcVector3 x(1.f,0.f,0.f);
IfcVector3 x(1.f, 0.f, 0.f);
if (in.RefDirection) {
ConvertDirection(x,*in.RefDirection.Get());
ConvertDirection(x, *in.RefDirection.Get());
}
const IfcVector3 y = IfcVector3(x.y,-x.x,0.f);
const IfcVector3 y = IfcVector3(x.y, -x.x, 0.f);
IfcMatrix4::Translation(loc,out);
AssignMatrixAxes(out,x,y,IfcVector3(0.f,0.f,1.f));
IfcMatrix4::Translation(loc, out);
AssignMatrixAxes(out, x, y, IfcVector3(0.f, 0.f, 1.f));
}
// ------------------------------------------------------------------------------------------------
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const Schema_2x3::IfcAxis1Placement& in)
{
ConvertCartesianPoint(pos,in.Location);
void ConvertAxisPlacement(IfcVector3 &axis, IfcVector3 &pos, const Schema_2x3::IfcAxis1Placement &in) {
ConvertCartesianPoint(pos, in.Location);
if (in.Axis) {
ConvertDirection(axis,in.Axis.Get());
}
else {
axis = IfcVector3(0.f,0.f,1.f);
ConvertDirection(axis, in.Axis.Get());
} else {
axis = IfcVector3(0.f, 0.f, 1.f);
}
}
// ------------------------------------------------------------------------------------------------
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv)
{
if(const Schema_2x3::IfcAxis2Placement3D* pl3 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement3D>(conv.db)) {
ConvertAxisPlacement(out,*pl3);
}
else if(const Schema_2x3::IfcAxis2Placement2D* pl2 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement2D>(conv.db)) {
ConvertAxisPlacement(out,*pl2);
}
else {
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement &in, ConversionData &conv) {
if (const Schema_2x3::IfcAxis2Placement3D *pl3 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement3D>(conv.db)) {
ConvertAxisPlacement(out, *pl3);
} else if (const Schema_2x3::IfcAxis2Placement2D *pl2 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement2D>(conv.db)) {
ConvertAxisPlacement(out, *pl2);
} else {
IFCImporter::LogWarn("skipping unknown IfcAxis2Placement entity");
}
}
// ------------------------------------------------------------------------------------------------
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op)
{
void ConvertTransformOperator(IfcMatrix4 &out, const Schema_2x3::IfcCartesianTransformationOperator &op) {
IfcVector3 loc;
ConvertCartesianPoint(loc,op.LocalOrigin);
ConvertCartesianPoint(loc, op.LocalOrigin);
IfcVector3 x(1.f,0.f,0.f),y(0.f,1.f,0.f),z(0.f,0.f,1.f);
IfcVector3 x(1.f, 0.f, 0.f), y(0.f, 1.f, 0.f), z(0.f, 0.f, 1.f);
if (op.Axis1) {
ConvertDirection(x,*op.Axis1.Get());
ConvertDirection(x, *op.Axis1.Get());
}
if (op.Axis2) {
ConvertDirection(y,*op.Axis2.Get());
ConvertDirection(y, *op.Axis2.Get());
}
if (const Schema_2x3::IfcCartesianTransformationOperator3D* op2 = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3D>()) {
if(op2->Axis3) {
ConvertDirection(z,*op2->Axis3.Get());
if (const Schema_2x3::IfcCartesianTransformationOperator3D *op2 = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3D>()) {
if (op2->Axis3) {
ConvertDirection(z, *op2->Axis3.Get());
}
}
IfcMatrix4 locm;
IfcMatrix4::Translation(loc,locm);
AssignMatrixAxes(out,x,y,z);
IfcMatrix4::Translation(loc, locm);
AssignMatrixAxes(out, x, y, z);
IfcVector3 vscale;
if (const Schema_2x3::IfcCartesianTransformationOperator3DnonUniform* nuni = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3DnonUniform>()) {
vscale.x = nuni->Scale?op.Scale.Get():1.f;
vscale.y = nuni->Scale2?nuni->Scale2.Get():1.f;
vscale.z = nuni->Scale3?nuni->Scale3.Get():1.f;
}
else {
const IfcFloat sc = op.Scale?op.Scale.Get():1.f;
vscale = IfcVector3(sc,sc,sc);
if (const Schema_2x3::IfcCartesianTransformationOperator3DnonUniform *nuni = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3DnonUniform>()) {
vscale.x = nuni->Scale ? op.Scale.Get() : 1.f;
vscale.y = nuni->Scale2 ? nuni->Scale2.Get() : 1.f;
vscale.z = nuni->Scale3 ? nuni->Scale3.Get() : 1.f;
} else {
const IfcFloat sc = op.Scale ? op.Scale.Get() : 1.f;
vscale = IfcVector3(sc, sc, sc);
}
IfcMatrix4 s;
IfcMatrix4::Scaling(vscale,s);
IfcMatrix4::Scaling(vscale, s);
out = locm * out * s;
}
} // ! IFC
} // ! Assimp
} // namespace IFC
} // namespace Assimp
#endif

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -47,40 +46,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_IFCUTIL_H
#define INCLUDED_IFCUTIL_H
#include "AssetLib/IFC/IFCReaderGen_2x3.h"
#include "AssetLib/IFC/IFCLoader.h"
#include "AssetLib/IFC/IFCReaderGen_2x3.h"
#include "AssetLib/Step/STEPFile.h"
#include <assimp/mesh.h>
#include <assimp/material.h>
#include <assimp/mesh.h>
struct aiNode;
namespace Assimp {
namespace IFC {
typedef double IfcFloat;
// IfcFloat-precision math data types
typedef aiVector2t<IfcFloat> IfcVector2;
typedef aiVector3t<IfcFloat> IfcVector3;
typedef aiMatrix4x4t<IfcFloat> IfcMatrix4;
typedef aiMatrix3x3t<IfcFloat> IfcMatrix3;
typedef aiColor4t<IfcFloat> IfcColor4;
using IfcFloat = double;
// IfcFloat-precision math data types
using IfcVector2 = aiVector2t<IfcFloat>;
using IfcVector3 = aiVector3t<IfcFloat>;
using IfcMatrix4 = aiMatrix4x4t<IfcFloat>;
using IfcMatrix3 = aiMatrix3x3t<IfcFloat>;
using IfcColor4 = aiColor4t<IfcFloat>;
// ------------------------------------------------------------------------------------------------
// Helper for std::for_each to delete all heap-allocated items in a container
// ------------------------------------------------------------------------------------------------
template<typename T>
template <typename T>
struct delete_fun {
void operator()(T* del) {
void operator()(T *del) {
delete del;
}
};
// ------------------------------------------------------------------------------------------------
// Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
// ------------------------------------------------------------------------------------------------
@ -89,32 +85,29 @@ struct TempMesh {
std::vector<unsigned int> mVertcnt;
// utilities
aiMesh* ToMesh();
aiMesh *ToMesh();
void Clear();
void Transform(const IfcMatrix4& mat);
void Transform(const IfcMatrix4 &mat);
IfcVector3 Center() const;
void Append(const TempMesh& other);
void Append(const TempMesh &other);
bool IsEmpty() const;
void RemoveAdjacentDuplicates();
void RemoveDegenerates();
void FixupFaceOrientation();
static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
static IfcVector3 ComputePolygonNormal(const IfcVector3 *vtcs, size_t cnt, bool normalize = true);
IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
void Swap(TempMesh& other);
void ComputePolygonNormals(std::vector<IfcVector3> &normals, bool normalize = true, size_t ofs = 0) const;
void Swap(TempMesh &other);
};
inline
bool TempMesh::IsEmpty() const {
inline bool TempMesh::IsEmpty() const {
return mVerts.empty() && mVertcnt.empty();
}
// ------------------------------------------------------------------------------------------------
// Temporary representation of an opening in a wall or a floor
// ------------------------------------------------------------------------------------------------
struct TempOpening
{
struct TempOpening {
const IFC::Schema_2x3::IfcSolidModel *solid;
IfcVector3 extrusionDir;
@ -129,90 +122,98 @@ struct TempOpening
std::vector<IfcVector3> wallPoints;
// ------------------------------------------------------------------------------
TempOpening()
: solid()
, extrusionDir()
, profileMesh()
{
TempOpening() :
solid(),
extrusionDir(),
profileMesh() {
// empty
}
// ------------------------------------------------------------------------------
TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir,
std::shared_ptr<TempMesh> profileMesh,
std::shared_ptr<TempMesh> profileMesh2D)
: solid(solid)
, extrusionDir(extrusionDir)
, profileMesh(profileMesh)
, profileMesh2D(profileMesh2D)
{
TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
std::shared_ptr<TempMesh> profileMesh,
std::shared_ptr<TempMesh> profileMesh2D) :
solid(solid),
extrusionDir(extrusionDir),
profileMesh(profileMesh),
profileMesh2D(profileMesh2D) {
// empty
}
// ------------------------------------------------------------------------------
void Transform(const IfcMatrix4& mat); // defined later since TempMesh is not complete yet
void Transform(const IfcMatrix4 &mat); // defined later since TempMesh is not complete yet
// ------------------------------------------------------------------------------
// Helper to sort openings by distance from a given base point
struct DistanceSorter {
DistanceSorter(const IfcVector3& base) : base(base) {}
DistanceSorter(const IfcVector3 &base) :
base(base) {}
bool operator () (const TempOpening& a, const TempOpening& b) const {
return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength();
bool operator()(const TempOpening &a, const TempOpening &b) const {
return (a.profileMesh->Center() - base).SquareLength() < (b.profileMesh->Center() - base).SquareLength();
}
IfcVector3 base;
};
};
// ------------------------------------------------------------------------------------------------
// Intermediate data storage during conversion. Keeps everything and a bit more.
// ------------------------------------------------------------------------------------------------
struct ConversionData
{
ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings)
: len_scale(1.0)
, angle_scale(-1.0)
, db(db)
, proj(proj)
, out(out)
, settings(settings)
, apply_openings()
, collect_openings()
{}
struct ConversionData {
ConversionData(const STEP::DB &db, const IFC::Schema_2x3::IfcProject &proj, aiScene *out, const IFCImporter::Settings &settings) :
len_scale(1.0),
angle_scale(-1.0),
plane_angle_in_radians(true),
db(db),
proj(proj),
out(out),
wcs(),
meshes(),
materials(),
cached_meshes(),
cached_materials(),
settings(settings),
apply_openings(nullptr),
collect_openings(nullptr),
already_processed() {
// empty
}
~ConversionData() {
std::for_each(meshes.begin(),meshes.end(),delete_fun<aiMesh>());
std::for_each(materials.begin(),materials.end(),delete_fun<aiMaterial>());
std::for_each(meshes.begin(), meshes.end(), delete_fun<aiMesh>());
std::for_each(materials.begin(), materials.end(), delete_fun<aiMaterial>());
}
IfcFloat len_scale, angle_scale;
bool plane_angle_in_radians;
const STEP::DB& db;
const IFC::Schema_2x3::IfcProject& proj;
aiScene* out;
const STEP::DB &db;
const IFC::Schema_2x3::IfcProject &proj;
aiScene *out;
IfcMatrix4 wcs;
std::vector<aiMesh*> meshes;
std::vector<aiMaterial*> materials;
std::vector<aiMesh *> meshes;
std::vector<aiMaterial *> materials;
struct MeshCacheIndex {
const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex;
MeshCacheIndex() : item(nullptr), matindex(0) { }
MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { }
bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; }
bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
const IFC::Schema_2x3::IfcRepresentationItem *item;
unsigned int matindex;
MeshCacheIndex() :
item(nullptr), matindex(0) {}
MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem *i, unsigned int mi) :
item(i), matindex(mi) {}
bool operator==(const MeshCacheIndex &o) const { return item == o.item && matindex == o.matindex; }
bool operator<(const MeshCacheIndex &o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
};
typedef std::map<MeshCacheIndex, std::set<unsigned int> > MeshCache;
using MeshCache = std::map<MeshCacheIndex, std::set<unsigned int>>;
MeshCache cached_meshes;
typedef std::map<const IFC::Schema_2x3::IfcSurfaceStyle*, unsigned int> MaterialCache;
using MaterialCache = std::map<const IFC::Schema_2x3::IfcSurfaceStyle *, unsigned int>;
MaterialCache cached_materials;
const IFCImporter::Settings& settings;
const IFCImporter::Settings &settings;
// Intermediate arrays used to resolve openings in walls: only one of them
// can be given at a time. apply_openings if present if the current element
@ -220,34 +221,33 @@ struct ConversionData
// collect_openings is present only if the current element is an
// IfcOpeningElement, for which all the geometry needs to be preserved
// for later processing by a parent, which is a wall.
std::vector<TempOpening>* apply_openings;
std::vector<TempOpening>* collect_openings;
std::vector<TempOpening> *apply_openings;
std::vector<TempOpening> *collect_openings;
std::set<uint64_t> already_processed;
};
// ------------------------------------------------------------------------------------------------
// Binary predicate to compare vectors with a given, quadratic epsilon.
// ------------------------------------------------------------------------------------------------
struct FuzzyVectorCompare {
FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
bool operator()(const IfcVector3& a, const IfcVector3& b) {
return std::abs((a-b).SquareLength()) < epsilon;
FuzzyVectorCompare(IfcFloat epsilon) :
epsilon(epsilon) {}
bool operator()(const IfcVector3 &a, const IfcVector3 &b) {
return std::abs((a - b).SquareLength()) < epsilon;
}
const IfcFloat epsilon;
};
// ------------------------------------------------------------------------------------------------
// Ordering predicate to totally order R^2 vectors first by x and then by y
// ------------------------------------------------------------------------------------------------
struct XYSorter {
// sort first by X coordinates, then by Y coordinates
bool operator () (const IfcVector2&a, const IfcVector2& b) const {
bool operator()(const IfcVector2 &a, const IfcVector2 &b) const {
if (a.x == b.x) {
return a.y < b.y;
}
@ -255,67 +255,61 @@ struct XYSorter {
}
};
// conversion routines for common IFC entities, implemented in IFCUtil.cpp
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in);
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base);
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in);
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in);
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in);
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z);
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in);
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in);
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& in);
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv);
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op);
bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in);
IfcFloat ConvertSIPrefix(const std::string& prefix);
void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourRgb &in);
void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourOrFactor &in, ConversionData &conv, const aiColor4D *base);
void ConvertCartesianPoint(IfcVector3 &out, const Schema_2x3::IfcCartesianPoint &in);
void ConvertDirection(IfcVector3 &out, const Schema_2x3::IfcDirection &in);
void ConvertVector(IfcVector3 &out, const Schema_2x3::IfcVector &in);
void AssignMatrixAxes(IfcMatrix4 &out, const IfcVector3 &x, const IfcVector3 &y, const IfcVector3 &z);
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement3D &in);
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement2D &in);
void ConvertAxisPlacement(IfcVector3 &axis, IfcVector3 &pos, const IFC::Schema_2x3::IfcAxis1Placement &in);
void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement &in, ConversionData &conv);
void ConvertTransformOperator(IfcMatrix4 &out, const Schema_2x3::IfcCartesianTransformationOperator &op);
bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN &in);
IfcFloat ConvertSIPrefix(const std::string &prefix);
// IFCProfile.cpp
bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, ConversionData& conv);
bool ProcessProfile(const Schema_2x3::IfcProfileDef &prof, TempMesh &meshout, ConversionData &conv);
bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
// IFCMaterial.cpp
unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData &conv, bool forceDefaultMat);
// IFCGeometry.cpp
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set<unsigned int>& mesh_indices, ConversionData& conv);
void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh &curmesh, bool &ok, IfcVector3 &norOut);
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem &item, unsigned int matid, std::set<unsigned int> &mesh_indices, ConversionData &conv);
void AssignAddedMeshes(std::set<unsigned int> &mesh_indices, aiNode *nd, ConversionData & /*conv*/);
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout,
ConversionData& conv);
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid &swept, TempMesh &meshout,
ConversionData &conv);
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result,
ConversionData& conv, bool collect_openings);
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid &solid, TempMesh &result,
ConversionData &conv, bool collect_openings);
// IFCBoolean.cpp
void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv);
void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result,
const TempMesh& first_operand,
ConversionData& conv);
void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace* hs, TempMesh& result,
const TempMesh& first_operand,
ConversionData& conv);
void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid* as, TempMesh& result,
const TempMesh& first_operand,
ConversionData& conv);
void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &result, ConversionData &conv);
void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid *hs, TempMesh &result,
const TempMesh &first_operand,
ConversionData &conv);
void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace *hs, TempMesh &result,
const TempMesh &first_operand,
ConversionData &conv);
void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid *as, TempMesh &result,
const TempMesh &first_operand,
ConversionData &conv);
// IFCOpenings.cpp
bool GenerateOpenings(std::vector<TempOpening>& openings,
const std::vector<IfcVector3>& nors,
TempMesh& curmesh,
bool check_intersection,
bool generate_connection_geometry,
const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0));
bool GenerateOpenings(std::vector<TempOpening> &openings,
const std::vector<IfcVector3> &nors,
TempMesh &curmesh,
bool check_intersection,
bool generate_connection_geometry,
const IfcVector3 &wall_extrusion_axis = IfcVector3(0, 1, 0));
// IFCCurve.cpp
@ -324,8 +318,8 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
// ------------------------------------------------------------------------------------------------
class CurveError {
public:
CurveError(const std::string& s)
: mStr(s) {
CurveError(const std::string &s) :
mStr(s) {
// empty
}
@ -338,18 +332,17 @@ public:
// ------------------------------------------------------------------------------------------------
class Curve {
protected:
Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv)
: base_entity(base_entity)
, conv(conv) {
Curve(const Schema_2x3::IfcCurve &base_entity, ConversionData &conv) :
base_entity(base_entity),
conv(conv) {
// empty
}
public:
typedef std::pair<IfcFloat, IfcFloat> ParamRange;
using ParamRange = std::pair<IfcFloat, IfcFloat>;
virtual ~Curve() {}
// check if a curve is closed
virtual bool IsClosed() const = 0;
@ -359,56 +352,53 @@ public:
// try to match a point on the curve to a given parameter
// for self-intersecting curves, the result is not ambiguous and
// it is undefined which parameter is returned.
virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const;
virtual bool ReverseEval(const IfcVector3 &val, IfcFloat &paramOut) const;
// get the range of the curve (both inclusive).
// +inf and -inf are valid return values, the curve is not bounded in such a case.
virtual std::pair<IfcFloat,IfcFloat> GetParametricRange() const = 0;
virtual std::pair<IfcFloat, IfcFloat> GetParametricRange() const = 0;
IfcFloat GetParametricRangeDelta() const;
// estimate the number of sample points that this curve will require
virtual size_t EstimateSampleCount(IfcFloat start,IfcFloat end) const;
virtual size_t EstimateSampleCount(IfcFloat start, IfcFloat end) const;
// intelligently sample the curve based on the current settings
// and append the result to the mesh
virtual void SampleDiscrete(TempMesh& out,IfcFloat start,IfcFloat end) const;
virtual void SampleDiscrete(TempMesh &out, IfcFloat start, IfcFloat end) const;
#ifdef ASSIMP_BUILD_DEBUG
// check if a particular parameter value lies within the well-defined range
bool InRange(IfcFloat) const;
#endif
static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv);
static Curve *Convert(const IFC::Schema_2x3::IfcCurve &, ConversionData &conv);
protected:
const Schema_2x3::IfcCurve& base_entity;
ConversionData& conv;
const Schema_2x3::IfcCurve &base_entity;
ConversionData &conv;
};
// --------------------------------------------------------------------------------
// A BoundedCurve always holds the invariant that GetParametricRange()
// never returns infinite values.
// --------------------------------------------------------------------------------
class BoundedCurve : public Curve {
public:
BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv)
: Curve(entity,conv)
{}
BoundedCurve(const Schema_2x3::IfcBoundedCurve &entity, ConversionData &conv) :
Curve(entity, conv) {}
public:
bool IsClosed() const;
bool IsClosed() const override;
public:
// sample the entire curve
void SampleDiscrete(TempMesh& out) const;
void SampleDiscrete(TempMesh &out) const;
using Curve::SampleDiscrete;
};
// IfcProfile.cpp
bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, ConversionData& conv);
}
}
bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
} // namespace IFC
} // namespace Assimp
#endif

File diff suppressed because it is too large Load Diff

View File

@ -44,20 +44,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_AI_IMPORTER_H
#define INCLUDED_AI_IMPORTER_H
#include <map>
#include <vector>
#include <string>
#include <assimp/matrix4x4.h>
#include <map>
#include <string>
#include <vector>
struct aiScene;
namespace Assimp {
class ProgressHandler;
class IOSystem;
class BaseImporter;
class BaseProcess;
class SharedPostProcessInfo;
namespace Assimp {
class ProgressHandler;
class IOSystem;
class BaseImporter;
class BaseProcess;
class SharedPostProcessInfo;
//! @cond never
// ---------------------------------------------------------------------------
@ -70,7 +69,7 @@ namespace Assimp {
class ImporterPimpl {
public:
// Data type to store the key hash
typedef unsigned int KeyType;
using KeyType = unsigned int;
// typedefs for our four configuration maps.
// We don't need more, so there is no need for a generic solution
@ -80,21 +79,21 @@ public:
typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
/** IO handler to use for all file accesses. */
IOSystem* mIOHandler;
IOSystem *mIOHandler;
bool mIsDefaultHandler;
/** Progress handler for feedback. */
ProgressHandler* mProgressHandler;
ProgressHandler *mProgressHandler;
bool mIsDefaultProgressHandler;
/** Format-specific importer worker objects - one for each format we can read.*/
std::vector< BaseImporter* > mImporter;
std::vector<BaseImporter *> mImporter;
/** Post processing steps we can apply at the imported data. */
std::vector< BaseProcess* > mPostProcessingSteps;
std::vector<BaseProcess *> mPostProcessingSteps;
/** The imported data, if ReadFile() was successful, nullptr otherwise. */
aiScene* mScene;
aiScene *mScene;
/** The error description, if there was one. In the case of an exception,
* mException will carry the full details. */
@ -120,29 +119,27 @@ public:
bool bExtraVerbose;
/** Used by post-process steps to share data */
SharedPostProcessInfo* mPPShared;
SharedPostProcessInfo *mPPShared;
/// The default class constructor.
ImporterPimpl() AI_NO_EXCEPT;
};
inline
ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT :
mIOHandler( nullptr ),
mIsDefaultHandler( false ),
mProgressHandler( nullptr ),
mIsDefaultProgressHandler( false ),
mImporter(),
mPostProcessingSteps(),
mScene( nullptr ),
mErrorString(),
mException(),
mIntProperties(),
mFloatProperties(),
mStringProperties(),
mMatrixProperties(),
bExtraVerbose( false ),
mPPShared( nullptr ) {
inline ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT : mIOHandler(nullptr),
mIsDefaultHandler(false),
mProgressHandler(nullptr),
mIsDefaultProgressHandler(false),
mImporter(),
mPostProcessingSteps(),
mScene(nullptr),
mErrorString(),
mException(),
mIntProperties(),
mFloatProperties(),
mStringProperties(),
mMatrixProperties(),
bExtraVerbose(false),
mPPShared(nullptr) {
// empty
}
//! @endcond
@ -164,17 +161,17 @@ public:
/** Wraps a full list of configuration properties for an importer.
* Properties can be set using SetGenericProperty */
struct PropertyMap {
ImporterPimpl::IntPropertyMap ints;
ImporterPimpl::FloatPropertyMap floats;
ImporterPimpl::StringPropertyMap strings;
ImporterPimpl::MatrixPropertyMap matrices;
ImporterPimpl::IntPropertyMap ints;
ImporterPimpl::FloatPropertyMap floats;
ImporterPimpl::StringPropertyMap strings;
ImporterPimpl::MatrixPropertyMap matrices;
bool operator == (const PropertyMap& prop) const {
bool operator==(const PropertyMap &prop) const {
// fixme: really isocpp? gcc complains
return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices;
}
bool empty () const {
bool empty() const {
return ints.empty() && floats.empty() && strings.empty() && matrices.empty();
}
};
@ -184,7 +181,7 @@ public:
/** Construct a batch loader from a given IO system to be used
* to access external files
*/
explicit BatchLoader(IOSystem* pIO, bool validate = false );
explicit BatchLoader(IOSystem *pIO, bool validate = false);
// -------------------------------------------------------------------
/** The class destructor.
@ -195,7 +192,7 @@ public:
/** Sets the validation step. True for enable validation during postprocess.
* @param enable True for validation.
*/
void setValidation( bool enabled );
void setValidation(bool enabled);
// -------------------------------------------------------------------
/** Returns the current validation step.
@ -211,11 +208,10 @@ public:
* @return 'Load request channel' - an unique ID that can later
* be used to access the imported file data.
* @see GetImport */
unsigned int AddLoadRequest (
const std::string& file,
unsigned int steps = 0,
const PropertyMap *map = nullptr
);
unsigned int AddLoadRequest(
const std::string &file,
unsigned int steps = 0,
const PropertyMap *map = nullptr);
// -------------------------------------------------------------------
/** Get an imported scene.
@ -226,9 +222,8 @@ public:
* @param which LRWC returned by AddLoadRequest().
* @return nullptr if there is no scene with this file name
* in the queue of the scene hasn't been loaded yet. */
aiScene* GetImport(
unsigned int which
);
aiScene *GetImport(
unsigned int which);
// -------------------------------------------------------------------
/** Waits until all scenes have been loaded. This returns

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -45,185 +44,147 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_POLYTOOLS_H_INCLUDED
#define AI_POLYTOOLS_H_INCLUDED
#include <assimp/material.h>
#include <assimp/ai_assert.h>
#include <assimp/material.h>
#include <assimp/vector3.h>
namespace Assimp {
// -------------------------------------------------------------------------------
/** Compute the signed area of a triangle.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline double GetArea2D(const T& v1, const T& v2, const T& v3)
{
return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y));
}
template<class T>
class TBoundingBox2D {
T mMin, mMax;
// -------------------------------------------------------------------------------
/** Test if a given point p2 is on the left side of the line formed by p0-p1.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2)
{
return GetArea2D(p0,p2,p1) > 0;
}
// -------------------------------------------------------------------------------
/** Test if a given point is inside a given triangle in R2.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp)
{
// Point in triangle test using baryzentric coordinates
const aiVector2D v0 = p1 - p0;
const aiVector2D v1 = p2 - p0;
const aiVector2D v2 = pp - p0;
double dot00 = v0 * v0;
double dot01 = v0 * v1;
double dot02 = v0 * v2;
double dot11 = v1 * v1;
double dot12 = v1 * v2;
const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1);
}
// -------------------------------------------------------------------------------
/** Check whether the winding order of a given polygon is counter-clockwise.
* The function accepts an unconstrained template parameter, but is intended
* to be used only with aiVector2D and aiVector3D (z axis is ignored, only
* x and y are taken into account).
* @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++
*/
template <typename T>
inline bool IsCCW(T* in, size_t npoints) {
double aa, bb, cc, b, c, theta;
double convex_turn;
double convex_sum = 0;
ai_assert(npoints >= 3);
for (size_t i = 0; i < npoints - 2; i++) {
aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) +
((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y));
bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) +
((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y));
cc = ((in[i+2].x - in[i+1].x) *
(in[i+2].x - in[i+1].x)) +
((-in[i+2].y + in[i+1].y) *
(-in[i+2].y + in[i+1].y));
b = std::sqrt(bb);
c = std::sqrt(cc);
theta = std::acos((bb + cc - aa) / (2 * b * c));
if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) {
// if (convex(in[i].x, in[i].y,
// in[i+1].x, in[i+1].y,
// in[i+2].x, in[i+2].y)) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
convex_sum -= AI_MATH_PI_F - theta;
}
TBoundingBox2D( const T &min, const T &max ) :
mMin( min ),
mMax( max ) {
// empty
}
aa = ((in[1].x - in[npoints-2].x) *
(in[1].x - in[npoints-2].x)) +
((-in[1].y + in[npoints-2].y) *
(-in[1].y + in[npoints-2].y));
};
bb = ((in[0].x - in[npoints-2].x) *
(in[0].x - in[npoints-2].x)) +
((-in[0].y + in[npoints-2].y) *
(-in[0].y + in[npoints-2].y));
using BoundingBox2D = TBoundingBox2D<aiVector2D>;
cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) +
((-in[1].y + in[0].y) * (-in[1].y + in[0].y));
// -------------------------------------------------------------------------------
/// Compute the normal of an arbitrary polygon in R3.
///
/// The code is based on Newell's formula, that is a polygons normal is the ratio
/// of its area when projected onto the three coordinate axes.
///
/// @param out Receives the output normal
/// @param num Number of input vertices
/// @param x X data source. x[ofs_x*n] is the n'th element.
/// @param y Y data source. y[ofs_y*n] is the y'th element
/// @param z Z data source. z[ofs_z*n] is the z'th element
///
/// @note The data arrays must have storage for at least num+2 elements. Using
/// this method is much faster than the 'other' NewellNormal()
// -------------------------------------------------------------------------------
template <size_t ofs_x, size_t ofs_y, size_t ofs_z, typename TReal>
inline void NewellNormal(aiVector3t<TReal> &out, size_t num, TReal *x, TReal *y, TReal *z, size_t bufferSize) {
ai_assert(bufferSize > num);
b = std::sqrt(bb);
c = std::sqrt(cc);
theta = std::acos((bb + cc - aa) / (2 * b * c));
//if (convex(in[npoints-2].x, in[npoints-2].y,
// in[0].x, in[0].y,
// in[1].x, in[1].y)) {
if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
convex_sum -= AI_MATH_PI_F - theta;
if (nullptr == x || nullptr == y || nullptr == z || 0 == bufferSize || 0 == num) {
return;
}
return convex_sum >= (2 * AI_MATH_PI_F);
}
// -------------------------------------------------------------------------------
/** Compute the normal of an arbitrary polygon in R3.
*
* The code is based on Newell's formula, that is a polygons normal is the ratio
* of its area when projected onto the three coordinate axes.
*
* @param out Receives the output normal
* @param num Number of input vertices
* @param x X data source. x[ofs_x*n] is the n'th element.
* @param y Y data source. y[ofs_y*n] is the y'th element
* @param z Z data source. z[ofs_z*n] is the z'th element
*
* @note The data arrays must have storage for at least num+2 elements. Using
* this method is much faster than the 'other' NewellNormal()
*/
template <int ofs_x, int ofs_y, int ofs_z, typename TReal>
inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z)
{
// Duplicate the first two vertices at the end
x[(num+0)*ofs_x] = x[0];
x[(num+1)*ofs_x] = x[ofs_x];
x[(num + 0) * ofs_x] = x[0];
x[(num + 1) * ofs_x] = x[ofs_x];
y[(num+0)*ofs_y] = y[0];
y[(num+1)*ofs_y] = y[ofs_y];
y[(num + 0) * ofs_y] = y[0];
y[(num + 1) * ofs_y] = y[ofs_y];
z[(num+0)*ofs_z] = z[0];
z[(num+1)*ofs_z] = z[ofs_z];
z[(num + 0) * ofs_z] = z[0];
z[(num + 1) * ofs_z] = z[ofs_z];
TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
TReal *xptr = x + ofs_x, *xlow = x, *xhigh = x + ofs_x * 2;
TReal *yptr = y + ofs_y, *ylow = y, *yhigh = y + ofs_y * 2;
TReal *zptr = z + ofs_z, *zlow = z, *zhigh = z + ofs_z * 2;
for (int tmp=0; tmp < num; tmp++) {
sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
for (size_t tmp = 0; tmp < num; ++tmp ) {
sum_xy += (*xptr) * ((*yhigh) - (*ylow));
sum_yz += (*yptr) * ((*zhigh) - (*zlow));
sum_zx += (*zptr) * ((*xhigh) - (*xlow));
xptr += ofs_x;
xlow += ofs_x;
xptr += ofs_x;
xlow += ofs_x;
xhigh += ofs_x;
yptr += ofs_y;
ylow += ofs_y;
yptr += ofs_y;
ylow += ofs_y;
yhigh += ofs_y;
zptr += ofs_z;
zlow += ofs_z;
zptr += ofs_z;
zlow += ofs_z;
zhigh += ofs_z;
}
out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy);
out = aiVector3t<TReal>(sum_yz, sum_zx, sum_xy);
}
} // ! Assimp
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
template <class T>
inline aiMatrix4x4t<T> DerivePlaneCoordinateSpace(const aiVector3t<T> *vertices, size_t numVertices, bool &ok, aiVector3t<T> &norOut) {
const aiVector3t<T> *out = vertices;
aiMatrix4x4t<T> m;
ok = true;
const size_t s = numVertices;
const aiVector3t<T> &any_point = out[numVertices - 1u];
aiVector3t<T> nor;
// The input polygon is arbitrarily shaped, therefore we might need some tries
// until we find a suitable normal. Note that Newell's algorithm would give
// a more robust result, but this variant also gives us a suitable first
// axis for the 2D coordinate space on the polygon plane, exploiting the
// fact that the input polygon is nearly always a quad.
bool done = false;
size_t idx = 0;
for (size_t i = 0; !done && i < s - 2; done || ++i) {
idx = i;
for (size_t j = i + 1; j < s - 1; ++j) {
nor = -((out[i] - any_point) ^ (out[j] - any_point));
if (std::fabs(nor.Length()) > 1e-8f) {
done = true;
break;
}
}
}
if (!done) {
ok = false;
return m;
}
nor.Normalize();
norOut = nor;
aiVector3t<T> r = (out[idx] - any_point);
r.Normalize();
// Reconstruct orthonormal basis
// XXX use Gram Schmidt for increased robustness
aiVector3t<T> u = r ^ nor;
u.Normalize();
m.a1 = r.x;
m.a2 = r.y;
m.a3 = r.z;
m.b1 = u.x;
m.b2 = u.y;
m.b3 = u.z;
m.c1 = -nor.x;
m.c2 = -nor.y;
m.c3 = -nor.z;
return m;
}
} // namespace Assimp
#endif

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -45,25 +43,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the FindDegenerates post-process step.
*/
// internal headers
#include "ProcessHelper.h"
#include "FindDegenerates.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
using namespace Assimp;
//remove mesh at position 'index' from the scene
static void removeMesh(aiScene* pScene, unsigned const index);
static void removeMesh(aiScene *pScene, unsigned const index);
//correct node indices to meshes and remove references to deleted mesh
static void updateSceneGraph(aiNode* pNode, unsigned const index);
static void updateSceneGraph(aiNode *pNode, unsigned const index);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess()
: mConfigRemoveDegenerates( false )
, mConfigCheckAreaOfTriangle( false ){
FindDegeneratesProcess::FindDegeneratesProcess() :
mConfigRemoveDegenerates(false),
mConfigCheckAreaOfTriangle(false) {
// empty
}
@ -75,24 +71,23 @@ FindDegeneratesProcess::~FindDegeneratesProcess() {
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindDegenerates);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
void FindDegeneratesProcess::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_FD_REMOVE
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene) {
void FindDegeneratesProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
//Do not process point cloud, ExecuteOnMesh works only with faces data
if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
removeMesh(pScene, i);
@ -102,12 +97,12 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
}
static void removeMesh(aiScene* pScene, unsigned const index) {
static void removeMesh(aiScene *pScene, unsigned const index) {
//we start at index and copy the pointers one position forward
//save the mesh pointer to delete it later
auto delete_me = pScene->mMeshes[index];
for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) {
pScene->mMeshes[i] = pScene->mMeshes[i+1];
pScene->mMeshes[i] = pScene->mMeshes[i + 1];
}
pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr;
--(pScene->mNumMeshes);
@ -117,15 +112,15 @@ static void removeMesh(aiScene* pScene, unsigned const index) {
updateSceneGraph(pScene->mRootNode, index);
}
static void updateSceneGraph(aiNode* pNode, unsigned const index) {
static void updateSceneGraph(aiNode *pNode, unsigned const index) {
for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
if (pNode->mMeshes[i] > index) {
--(pNode->mMeshes[i]);
continue;
}
if (pNode->mMeshes[i] == index) {
for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) {
pNode->mMeshes[j] = pNode->mMeshes[j+1];
for (unsigned j = i; j < pNode->mNumMeshes - 1; ++j) {
pNode->mMeshes[j] = pNode->mMeshes[j + 1];
}
--(pNode->mNumMeshes);
--i;
@ -138,50 +133,50 @@ static void updateSceneGraph(aiNode* pNode, unsigned const index) {
}
}
static ai_real heron( ai_real a, ai_real b, ai_real c ) {
static ai_real heron(ai_real a, ai_real b, ai_real c) {
ai_real s = (a + b + c) / 2;
ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
ai_real area = pow((s * (s - a) * (s - b) * (s - c)), (ai_real)0.5);
return area;
}
static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
const ai_real lx = ( vB.x - vA.x );
const ai_real ly = ( vB.y - vA.y );
const ai_real lz = ( vB.z - vA.z );
ai_real a = lx*lx + ly*ly + lz*lz;
ai_real d = pow( a, (ai_real)0.5 );
static ai_real distance3D(const aiVector3D &vA, aiVector3D &vB) {
const ai_real lx = (vB.x - vA.x);
const ai_real ly = (vB.y - vA.y);
const ai_real lz = (vB.z - vA.z);
ai_real a = lx * lx + ly * ly + lz * lz;
ai_real d = pow(a, (ai_real)0.5);
return d;
}
static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
static ai_real calculateAreaOfTriangle(const aiFace &face, aiMesh *mesh) {
ai_real area = 0;
aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
aiVector3D vA(mesh->mVertices[face.mIndices[0]]);
aiVector3D vB(mesh->mVertices[face.mIndices[1]]);
aiVector3D vC(mesh->mVertices[face.mIndices[2]]);
ai_real a( distance3D( vA, vB ) );
ai_real b( distance3D( vB, vC ) );
ai_real c( distance3D( vC, vA ) );
area = heron( a, b, c );
ai_real a(distance3D(vA, vB));
ai_real b(distance3D(vB, vC));
ai_real c(distance3D(vC, vA));
area = heron(a, b, c);
return area;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh
bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me;
if (mConfigRemoveDegenerates) {
remove_me.resize( mesh->mNumFaces, false );
remove_me.resize(mesh->mNumFaces, false);
}
unsigned int deg = 0, limit;
for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
aiFace& face = mesh->mFaces[a];
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace &face = mesh->mFaces[a];
bool first = true;
// check whether the face contains degenerated entries
@ -191,43 +186,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
// double points may not come directly after another.
limit = face.mNumIndices;
if (face.mNumIndices > 4) {
limit = std::min( limit, i+2 );
limit = std::min(limit, i + 2);
}
for (unsigned int t = i+1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
for (unsigned int t = i + 1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
// we have found a matching vertex position
// remove the corresponding index from the array
--face.mNumIndices;
--limit;
for (unsigned int m = t; m < face.mNumIndices; ++m) {
face.mIndices[ m ] = face.mIndices[ m+1 ];
face.mIndices[m] = face.mIndices[m + 1];
}
--t;
// NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his
// application attempts to access this data.
face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first) {
if (first) {
++deg;
first = false;
}
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
if (mConfigRemoveDegenerates) {
remove_me[a] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
}
}
}
if ( mConfigCheckAreaOfTriangle ) {
if ( face.mNumIndices == 3 ) {
ai_real area = calculateAreaOfTriangle( face, mesh );
if ( area < 1e-6 ) {
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
if (mConfigCheckAreaOfTriangle) {
if (face.mNumIndices == 3) {
ai_real area = calculateAreaOfTriangle(face, mesh);
if (area < 1e-6) {
if (mConfigRemoveDegenerates) {
remove_me[a] = true;
++deg;
goto evil_jump_outside;
}
@ -239,8 +234,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
}
// We need to update the primitive flags array of the mesh.
switch (face.mNumIndices)
{
switch (face.mNumIndices) {
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
@ -254,30 +248,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
};
evil_jump_outside:
evil_jump_outside:
continue;
}
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (mConfigRemoveDegenerates && deg) {
unsigned int n = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face_src = mesh->mFaces[a];
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace &face_src = mesh->mFaces[a];
if (!remove_me[a]) {
aiFace& face_dest = mesh->mFaces[n++];
aiFace &face_dest = mesh->mFaces[n++];
// Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices;
face_dest.mIndices = face_src.mIndices;
face_dest.mIndices = face_src.mIndices;
if (&face_src != &face_dest) {
// clear source
face_src.mNumIndices = 0;
face_src.mIndices = nullptr;
}
}
else {
} else {
// Otherwise delete it if we don't need this face
delete[] face_src.mIndices;
face_src.mIndices = nullptr;
@ -295,7 +287,7 @@ evil_jump_outside:
}
if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives");
ASSIMP_LOG_WARN_F("Found ", deg, " degenerated primitives");
}
return false;
}

View File

@ -47,485 +47,294 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* The triangulation algorithm will handle concave or convex polygons.
* Self-intersecting or non-planar polygons are not rejected, but
* they're probably not triangulated correctly.
*
* DEBUG SWITCHES - do not enable any of them in release builds:
*
* AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
* - generates vertex colors to represent the face winding order.
* the first vertex of a polygon becomes red, the last blue.
* AI_BUILD_TRIANGULATE_DEBUG_POLYS
* - dump all polygons and their triangulation sequences to
* a file
*/
#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
#include "PostProcessing/TriangulateProcess.h"
#include "PostProcessing/ProcessHelper.h"
#include "Common/PolyTools.h"
#include "PostProcessing/ProcessHelper.h"
#include "contrib/poly2tri/poly2tri/poly2tri.h"
#include <memory>
#include <cstdint>
#include <memory>
//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS
#define POLY_GRID_Y 40
#define POLY_GRID_X 70
#define POLY_GRID_XPAD 20
#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt"
using namespace Assimp;
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TriangulateProcess::TriangulateProcess()
{
TriangulateProcess::TriangulateProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
TriangulateProcess::~TriangulateProcess()
{
TriangulateProcess::~TriangulateProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool TriangulateProcess::IsActive( unsigned int pFlags) const
{
bool TriangulateProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_Triangulate) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void TriangulateProcess::Execute( aiScene* pScene)
{
void TriangulateProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("TriangulateProcess begin");
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
if (pScene->mMeshes[ a ]) {
if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) {
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if (pScene->mMeshes[a]) {
if (TriangulateMesh(pScene->mMeshes[a])) {
bHas = true;
}
}
}
if ( bHas ) {
ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." );
if (bHas) {
ASSIMP_LOG_INFO("TriangulateProcess finished. All polygons have been triangulated.");
} else {
ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." );
ASSIMP_LOG_DEBUG("TriangulateProcess finished. There was nothing to be done.");
}
}
// ------------------------------------------------------------------------------------------------
// Triangulates the given mesh.
bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
{
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
if (!pMesh->mPrimitiveTypes) {
bool bNeed = false;
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace& face = pMesh->mFaces[a];
if( face.mNumIndices != 3) {
bNeed = true;
}
static bool validateNumIndices(aiMesh *mesh) {
bool bNeed = false;
for (unsigned int a = 0; a < mesh->mNumFaces; a++) {
const aiFace &face = mesh->mFaces[a];
if (face.mNumIndices != 3) {
bNeed = true;
break;
}
if (!bNeed)
return false;
}
else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
return bNeed;
}
// ------------------------------------------------------------------------------------------------
static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, bool &getNormals) {
numOut = maxOut = 0;
getNormals = true;
for (unsigned int a = 0; a < mesh->mNumFaces; a++) {
aiFace &face = mesh->mFaces[a];
if (face.mNumIndices <= 4) {
getNormals = false;
}
if (face.mNumIndices <= 3) {
numOut++;
} else {
numOut += face.mNumIndices - 2;
maxOut = std::max(maxOut, static_cast<size_t>(face.mNumIndices));
}
}
}
// ------------------------------------------------------------------------------------------------
static void quad2Triangles(const aiFace &face, const aiVector3D *verts, aiFace *curOut) {
// quads can have at maximum one concave vertex. Determine
// this vertex (if it exists) and start tri-fanning from
// it.
unsigned int start_vertex = 0;
for (unsigned int i = 0; i < 4; ++i) {
const aiVector3D &v0 = verts[face.mIndices[(i + 3) % 4]];
const aiVector3D &v1 = verts[face.mIndices[(i + 2) % 4]];
const aiVector3D &v2 = verts[face.mIndices[(i + 1) % 4]];
const aiVector3D &v = verts[face.mIndices[i]];
aiVector3D left = (v0 - v);
aiVector3D diag = (v1 - v);
aiVector3D right = (v2 - v);
left.Normalize();
diag.Normalize();
right.Normalize();
const float angle = std::acos(left * diag) + std::acos(right * diag);
if (angle > AI_MATH_PI_F) {
// this is the concave point
start_vertex = i;
break;
}
}
const unsigned int temp[] = { face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3] };
aiFace &nface = *curOut++;
nface.mNumIndices = 3;
nface.mIndices = face.mIndices;
nface.mIndices[0] = temp[start_vertex];
nface.mIndices[1] = temp[(start_vertex + 1) % 4];
nface.mIndices[2] = temp[(start_vertex + 2) % 4];
aiFace &sface = *curOut++;
sface.mNumIndices = 3;
sface.mIndices = new unsigned int[3];
sface.mIndices[0] = temp[start_vertex];
sface.mIndices[1] = temp[(start_vertex + 2) % 4];
sface.mIndices[2] = temp[(start_vertex + 3) % 4];
}
// ------------------------------------------------------------------------------------------------
bool getContourFromePolyline(aiFace &face, aiMesh *pMesh, std::vector<p2t::Point *> &contour,
aiMatrix4x4 &m, aiVector3D &vmin, aiVector3D &vmax, ai_real &zcoord) {
aiVector3D normal;
bool ok = true;
m = DerivePlaneCoordinateSpace<ai_real>(pMesh->mVertices, pMesh->mNumVertices, ok, normal);
if (!ok) {
false;
}
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
unsigned int index = face.mIndices[i];
const aiVector3D vv = m * pMesh->mVertices[index];
// keep Z offset in the plane coordinate system. Ignoring precision issues
// (which are present, of course), this should be the same value for
// all polygon vertices (assuming the polygon is planar).
// XXX this should be guarded, but we somehow need to pick a suitable
// epsilon
// if(coord != -1.0f) {
// assert(std::fabs(coord - vv.z) < 1e-3f);
// }
zcoord += vv.z;
vmin = std::min(vv, vmin);
vmax = std::max(vv, vmax);
contour.push_back(new p2t::Point(vv.x, vv.y));
}
zcoord /= pMesh->mNumVertices;
// Further improve the projection by mapping the entire working set into
// [0,1] range. This gives us a consistent data range so all epsilons
// used below can be constants.
vmax -= vmin;
const aiVector2D one_vec(1, 1);
for (p2t::Point* &vv : contour) {
vv->x = (vv->x - vmin.x) / vmax.x;
vv->y = (vv->y - vmin.y) / vmax.y;
// sanity rounding
aiVector2D cur_vv((ai_real) vv->x, (ai_real)vv->y);
cur_vv = std::max(cur_vv, aiVector2D());
cur_vv = std::min(cur_vv, one_vec);
}
aiMatrix4x4 mult;
mult.a1 = static_cast<ai_real>(1.0) / vmax.x;
mult.b2 = static_cast<ai_real>(1.0) / vmax.y;
mult.a4 = -vmin.x * mult.a1;
mult.b4 = -vmin.y * mult.b2;
mult.c4 = -zcoord;
m = mult * m;
return true;
}
// ------------------------------------------------------------------------------------------------
// Triangulates the given mesh.
bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
if (!pMesh->mPrimitiveTypes) {
if (!validateNumIndices(pMesh)) {
ASSIMP_LOG_DEBUG("Error while validating number of indices.");
return false;
}
} else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
ASSIMP_LOG_DEBUG("???!");
return false;
}
// Find out how many output faces we'll get
uint32_t numOut = 0, max_out = 0;
bool get_normals = true;
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices <= 4) {
get_normals = false;
}
if( face.mNumIndices <= 3) {
numOut++;
}
else {
numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices);
}
}
// Just another check whether aiMesh::mPrimitiveTypes is correct
ai_assert(numOut != pMesh->mNumFaces);
aiVector3D *nor_out = nullptr;
// if we don't have normals yet, but expect them to be a cheap side
// product of triangulation anyway, allocate storage for them.
if (!pMesh->mNormals && get_normals) {
// XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals
// nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
size_t numOut = 0, max_out = 0;
bool getNormals = true;
calulateNumOutputFaces(pMesh, numOut, max_out, getNormals);
if (numOut == pMesh->mNumFaces) {
ASSIMP_LOG_DEBUG("Error while generating contour.");
return false;
}
// the output mesh will contain triangles, but no polys anymore
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
aiFace* out = new aiFace[numOut](), *curOut = out;
std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */
std::vector<aiVector2D> temp_verts(max_out+2);
aiFace *out = new aiFace[numOut](), *curOut = out;
const size_t Capa = max_out + 2;
std::vector<aiVector3D> temp_verts3d(max_out + 2); /* temporary storage for vertices */
std::vector<aiVector2D> temp_verts(max_out + 2);
// Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
if (!pMesh->mColors[0])
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
else
new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices];
aiColor4D* clr = pMesh->mColors[0];
#endif
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
FILE* fout = fopen(POLY_OUTPUT_FILE,"a");
#endif
const aiVector3D* verts = pMesh->mVertices;
const aiVector3D *verts = pMesh->mVertices;
// use std::unique_ptr to avoid slow std::vector<bool> specialiations
std::unique_ptr<bool[]> done(new bool[max_out]);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
unsigned int* idx = face.mIndices;
int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num;
// Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
aiColor4D& c = clr[idx[i]];
c.r = (i+1) / (float)max;
c.b = 1.f - c.r;
}
#endif
aiFace* const last_face = curOut;
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace &face = pMesh->mFaces[a];
// if it's a simple point,line or triangle: just copy it
if( face.mNumIndices <= 3)
{
aiFace& nface = *curOut++;
if (face.mNumIndices <= 3) {
aiFace &nface = *curOut++;
nface.mNumIndices = face.mNumIndices;
nface.mIndices = face.mIndices;
face.mIndices = nullptr;
continue;
}
// optimized code for quadrilaterals
else if ( face.mNumIndices == 4) {
// quads can have at maximum one concave vertex. Determine
// this vertex (if it exists) and start tri-fanning from
// it.
unsigned int start_vertex = 0;
for (unsigned int i = 0; i < 4; ++i) {
const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]];
const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]];
const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]];
const aiVector3D& v = verts[face.mIndices[i]];
aiVector3D left = (v0-v);
aiVector3D diag = (v1-v);
aiVector3D right = (v2-v);
left.Normalize();
diag.Normalize();
right.Normalize();
const float angle = std::acos(left*diag) + std::acos(right*diag);
if (angle > AI_MATH_PI_F) {
// this is the concave point
start_vertex = i;
break;
}
}
const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]};
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
nface.mIndices = face.mIndices;
nface.mIndices[0] = temp[start_vertex];
nface.mIndices[1] = temp[(start_vertex + 1) % 4];
nface.mIndices[2] = temp[(start_vertex + 2) % 4];
aiFace& sface = *curOut++;
sface.mNumIndices = 3;
sface.mIndices = new unsigned int[3];
sface.mIndices[0] = temp[start_vertex];
sface.mIndices[1] = temp[(start_vertex + 2) % 4];
sface.mIndices[2] = temp[(start_vertex + 3) % 4];
// prevent double deletion of the indices field
face.mIndices = nullptr;
continue;
} else if (face.mNumIndices == 4) {
// optimized code for quadrilaterals
quad2Triangles(face, verts, curOut);
face.mIndices = nullptr;
} else {
std::vector<p2t::Point *> contour;
aiMatrix4x4 m;
aiVector3D vmin, vmax;
ai_real zcoord = -1;
if (!getContourFromePolyline(face, pMesh, contour, m, vmin, vmax, zcoord)) {
ASSIMP_LOG_DEBUG("Error while generating contour.");
continue;
}
p2t::CDT cdt(contour);
cdt.Triangulate();
const std::vector<p2t::Triangle *> tris = cdt.GetTriangles();
const aiMatrix4x4 matInv = m.Inverse();
for (p2t::Triangle *tri : tris) {
curOut->mNumIndices = 3;
curOut->mIndices = new unsigned int[curOut->mNumIndices];
for (int i = 0; i < 3; ++i) {
const aiVector2D v = aiVector2D(static_cast<ai_real>(tri->GetPoint(i)->x), static_cast<ai_real>(tri->GetPoint(i)->y));
// ai_assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0);
const aiVector3D v3 = matInv * aiVector3D(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y, zcoord);
temp_verts3d.emplace_back(v3);
curOut->mIndices[i] = (unsigned int) temp_verts3d.size()-1;
}
curOut++;
}
face.mIndices = nullptr;
}
else
{
// A polygon with more than 3 vertices can be either concave or convex.
// Usually everything we're getting is convex and we could easily
// triangulate by tri-fanning. However, LightWave is probably the only
// modeling suite to make extensive use of highly concave, monster polygons ...
// so we need to apply the full 'ear cutting' algorithm to get it right.
// RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
// We project it onto a plane to get a 2d triangle.
// Collect all vertices of of the polygon.
for (tmp = 0; tmp < max; ++tmp) {
temp_verts3d[tmp] = verts[idx[tmp]];
}
// Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh
aiVector3D n;
NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z);
if (nor_out) {
for (tmp = 0; tmp < max; ++tmp)
nor_out[idx[tmp]] = n;
}
// Select largest normal coordinate to ignore for projection
const float ax = (n.x>0 ? n.x : -n.x);
const float ay = (n.y>0 ? n.y : -n.y);
const float az = (n.z>0 ? n.z : -n.z);
unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */
float inv = n.z;
if (ax > ay) {
if (ax > az) { /* no x coord. projection to yz */
ac = 1; bc = 2;
inv = n.x;
}
}
else if (ay > az) { /* no y coord. projection to zy */
ac = 2; bc = 0;
inv = n.y;
}
// Swap projection axes to take the negated projection vector into account
if (inv < 0.f) {
std::swap(ac,bc);
}
for (tmp =0; tmp < max; ++tmp) {
temp_verts[tmp].x = verts[idx[tmp]][ac];
temp_verts[tmp].y = verts[idx[tmp]][bc];
done[tmp] = false;
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
// plot the plane onto which we mapped the polygon to a 2D ASCII pic
aiVector2D bmin,bmax;
ArrayBounds(&temp_verts[0],max,bmin,bmax);
char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD];
std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' ');
for (int i =0; i < max; ++i) {
const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin);
const size_t x = static_cast<size_t>(v.x*(POLY_GRID_X-1)), y = static_cast<size_t>(v.y*(POLY_GRID_Y-1));
char* loc = grid[y]+x;
if (grid[y][x] != ' ') {
for(;*loc != ' '; ++loc);
*loc++ = '_';
}
*(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' ';
}
for(size_t y = 0; y < POLY_GRID_Y; ++y) {
grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0';
fprintf(fout,"%s\n",grid[y]);
}
fprintf(fout,"\ntriangulation sequence: ");
#endif
//
// FIXME: currently this is the slow O(kn) variant with a worst case
// complexity of O(n^2) (I think). Can be done in O(n).
while (num > 3) {
// Find the next ear of the polygon
int num_found = 0;
for (ear = next;;prev = ear,ear = next) {
// break after we looped two times without a positive match
for (next=ear+1;done[(next>=max?next=0:next)];++next);
if (next < ear) {
if (++num_found == 2) {
break;
}
}
const aiVector2D* pnt1 = &temp_verts[ear],
*pnt0 = &temp_verts[prev],
*pnt2 = &temp_verts[next];
// Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) {
continue;
}
// and no other point may be contained in this triangle
for ( tmp = 0; tmp < max; ++tmp) {
// We need to compare the actual values because it's possible that multiple indexes in
// the polygon are referring to the same position. concave_polygon.obj is a sample
//
// FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
// PointInTriangle() I'm guessing that it's actually possible to construct
// input data that would cause us to end up with no ears. The problem is,
// which epsilon? If we chose a too large value, we'd get wrong results
const aiVector2D& vtmp = temp_verts[tmp];
if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) {
break;
}
}
if (tmp != max) {
continue;
}
// this vertex is an ear
break;
}
if (num_found == 2) {
// Due to the 'two ear theorem', every simple polygon with more than three points must
// have 2 'ears'. Here's definitely something wrong ... but we don't give up yet.
//
// Instead we're continuing with the standard tri-fanning algorithm which we'd
// use if we had only convex polygons. That's life.
ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
fprintf(fout,"critical error here, no ear found! ");
#endif
num = 0;
break;
/*curOut -= (max-num); // undo all previous work
for (tmp = 0; tmp < max-2; ++tmp) {
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices)
nface.mIndices = new unsigned int[3];
nface.mIndices[0] = 0;
nface.mIndices[1] = tmp+1;
nface.mIndices[2] = tmp+2;
}
num = 0;
break;*/
}
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices) {
nface.mIndices = new unsigned int[3];
}
// setup indices for the new triangle ...
nface.mIndices[0] = prev;
nface.mIndices[1] = ear;
nface.mIndices[2] = next;
// exclude the ear from most further processing
done[ear] = true;
--num;
}
if (num > 0) {
// We have three indices forming the last 'ear' remaining. Collect them.
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices) {
nface.mIndices = new unsigned int[3];
}
for (tmp = 0; done[tmp]; ++tmp);
nface.mIndices[0] = tmp;
for (++tmp; done[tmp]; ++tmp);
nface.mIndices[1] = tmp;
for (++tmp; done[tmp]; ++tmp);
nface.mIndices[2] = tmp;
}
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
for(aiFace* f = last_face; f != curOut; ++f) {
unsigned int* i = f->mIndices;
fprintf(fout," (%i %i %i)",i[0],i[1],i[2]);
}
fprintf(fout,"\n*********************************************************************\n");
fflush(fout);
#endif
for(aiFace* f = last_face; f != curOut; ) {
unsigned int* i = f->mIndices;
// drop dumb 0-area triangles - deactivated for now:
//FindDegenerates post processing step can do the same thing
//if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
// ASSIMP_LOG_VERBOSE_DEBUG("Dropping triangle with area 0");
// --curOut;
// delete[] f->mIndices;
// f->mIndices = nullptr;
// for(aiFace* ff = f; ff != curOut; ++ff) {
// ff->mNumIndices = (ff+1)->mNumIndices;
// ff->mIndices = (ff+1)->mIndices;
// (ff+1)->mIndices = nullptr;
// }
// continue;
//}
i[0] = idx[i[0]];
i[1] = idx[i[1]];
i[2] = idx[i[2]];
++f;
}
delete[] face.mIndices;
face.mIndices = nullptr;
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
fclose(fout);
#endif
delete[] pMesh->mFaces;
pMesh->mFaces = out;
pMesh->mNumVertices = (unsigned int)temp_verts3d.size();
delete[] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (size_t i = 0; i < temp_verts3d.size(); ++i) {
pMesh->mVertices[i] = temp_verts3d[i];
}
pMesh->mNumFaces = (unsigned int)(curOut - out); /* not necessarily equal to numOut */
// kill the old faces
delete [] pMesh->mFaces;
// ... and store the new ones
pMesh->mFaces = out;
pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */
return true;
}
} // namespace Assimp
#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS

Binary file not shown.

View File

@ -1,5 +1,8 @@
find_package( Doxygen REQUIRED )
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
set( HTML_OUTPUT "AssimpDoc_Html" CACHE STRING "Output directory for generated HTML documentation. Defaults to AssimpDoc_Html." )
# Enable Microsoft CHM help style only on Windows

View File

@ -1484,7 +1484,7 @@ MAN_LINKS = NO
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be

View File

@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pragma GCC system_header
#endif
#include <stdint.h>
#include <string.h>
#include <cstdint>
#include <cstring>
// ------------------------------------------------------------------------------------------------
// Hashing function taken from
@ -74,8 +74,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ------------------------------------------------------------------------------------------------
inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) {
uint32_t tmp;
int rem;
uint32_t tmp;
int rem;
if (!data) return 0;
if (!len)len = (uint32_t)::strlen(data);

View File

@ -49,12 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_LINE_SPLITTER_H
#ifdef __GNUC__
# pragma GCC system_header
#pragma GCC system_header
#endif
#include <stdexcept>
#include <assimp/StreamReader.h>
#include <assimp/ParsingUtils.h>
#include <assimp/StreamReader.h>
#include <stdexcept>
namespace Assimp {
@ -79,37 +79,37 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
// ------------------------------------------------------------------------------------------------
class LineSplitter {
public:
typedef size_t line_idx;
using line_idx = size_t;
// -----------------------------------------
/** construct from existing stream reader
note: trim is *always* assumed true if skyp_empty_lines==true
*/
LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
LineSplitter(StreamReaderLE &stream, bool skip_empty_lines = true, bool trim = true);
~LineSplitter();
// -----------------------------------------
/** pseudo-iterator increment */
LineSplitter& operator++();
LineSplitter &operator++();
// -----------------------------------------
LineSplitter& operator++(int);
LineSplitter &operator++(int);
// -----------------------------------------
/** get a pointer to the beginning of a particular token */
const char* operator[] (size_t idx) const;
const char *operator[](size_t idx) const;
// -----------------------------------------
/** extract the start positions of N tokens from the current line*/
template <size_t N>
void get_tokens(const char* (&tokens)[N]) const;
void get_tokens(const char *(&tokens)[N]) const;
// -----------------------------------------
/** member access */
const std::string* operator -> () const;
const std::string *operator->() const;
std::string operator* () const;
std::string operator*() const;
// -----------------------------------------
/** boolean context */
@ -123,47 +123,45 @@ public:
// -----------------------------------------
/** access the underlying stream object */
StreamReaderLE& get_stream();
StreamReaderLE &get_stream();
// -----------------------------------------
/** !strcmp((*this)->substr(0,strlen(check)),check) */
bool match_start(const char* check);
bool match_start(const char *check);
// -----------------------------------------
/** swallow the next call to ++, return the previous value. */
void swallow_next_increment();
LineSplitter( const LineSplitter & ) = delete;
LineSplitter(const LineSplitter &) = delete;
LineSplitter(LineSplitter &&) = delete;
LineSplitter &operator = ( const LineSplitter & ) = delete;
LineSplitter &operator=(const LineSplitter &) = delete;
private:
line_idx mIdx;
std::string mCur;
StreamReaderLE& mStream;
StreamReaderLE &mStream;
bool mSwallow, mSkip_empty_lines, mTrim;
};
AI_FORCE_INLINE
LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim )
: mIdx(0)
, mCur()
, mStream(stream)
, mSwallow()
, mSkip_empty_lines(skip_empty_lines)
, mTrim(trim) {
AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE &stream, bool skip_empty_lines, bool trim) :
mIdx(0),
mCur(),
mStream(stream),
mSwallow(),
mSkip_empty_lines(skip_empty_lines),
mTrim(trim) {
mCur.reserve(1024);
operator++();
mIdx = 0;
}
AI_FORCE_INLINE
LineSplitter::~LineSplitter() {
AI_FORCE_INLINE LineSplitter::~LineSplitter() {
// empty
}
AI_FORCE_INLINE
LineSplitter& LineSplitter::operator++() {
LineSplitter &LineSplitter::operator++() {
if (mSwallow) {
mSwallow = false;
return *this;
@ -178,7 +176,8 @@ LineSplitter& LineSplitter::operator++() {
while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
if (s == '\n' || s == '\r') {
if (mSkip_empty_lines) {
while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'));
while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'))
;
if (mStream.GetRemainingSize()) {
mStream.IncPtr(-1);
}
@ -188,7 +187,8 @@ LineSplitter& LineSplitter::operator++() {
mStream.IncPtr(-1);
}
if (mTrim) {
while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'));
while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'))
;
if (mStream.GetRemainingSize()) {
mStream.IncPtr(-1);
}
@ -203,14 +203,13 @@ LineSplitter& LineSplitter::operator++() {
return *this;
}
AI_FORCE_INLINE
LineSplitter &LineSplitter::operator++(int) {
AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
return ++(*this);
}
AI_FORCE_INLINE
const char *LineSplitter::operator[] (size_t idx) const {
const char* s = operator->()->c_str();
const char *LineSplitter::operator[](size_t idx) const {
const char *s = operator->()->c_str();
SkipSpaces(&s);
for (size_t i = 0; i < idx; ++i) {
@ -226,9 +225,8 @@ const char *LineSplitter::operator[] (size_t idx) const {
}
template <size_t N>
AI_FORCE_INLINE
void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
const char* s = operator->()->c_str();
AI_FORCE_INLINE void LineSplitter::get_tokens(const char *(&tokens)[N]) const {
const char *s = operator->()->c_str();
SkipSpaces(&s);
for (size_t i = 0; i < N; ++i) {
@ -237,18 +235,19 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
}
tokens[i] = s;
for (; *s && !IsSpace(*s); ++s);
for (; *s && !IsSpace(*s); ++s)
;
SkipSpaces(&s);
}
}
AI_FORCE_INLINE
const std::string* LineSplitter::operator -> () const {
const std::string *LineSplitter::operator->() const {
return &mCur;
}
AI_FORCE_INLINE
std::string LineSplitter::operator* () const {
std::string LineSplitter::operator*() const {
return mCur;
}
@ -273,7 +272,7 @@ StreamReaderLE &LineSplitter::get_stream() {
}
AI_FORCE_INLINE
bool LineSplitter::match_start(const char* check) {
bool LineSplitter::match_start(const char *check) {
const size_t len = ::strlen(check);
return len <= mCur.length() && std::equal(check, check + len, mCur.begin());

View File

@ -119,7 +119,7 @@ public:
/** @brief Normalize the vector with extra check for zero vectors */
aiVector3t& NormalizeSafe();
/** @brief Componentwise multiplication of two vectors
/** @brief Component-wise multiplication of two vectors
*
* Note that vec*vec yields the dot product.
* @param o Second factor */
@ -129,7 +129,7 @@ public:
};
typedef aiVector3t<ai_real> aiVector3D;
using aiVector3D = aiVector3t<ai_real>;
#else

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -306,4 +304,5 @@ aiVector3t<TReal> operator - ( const aiVector3t<TReal>& v) {
// ------------------------------------------------------------------------------------------------
#endif // __cplusplus
#endif // AI_VECTOR3D_INL_INC

View File

@ -96,6 +96,7 @@ SET( COMMON
unit/Common/utSpatialSort.cpp
unit/Common/utAssertHandler.cpp
unit/Common/utXmlParser.cpp
unit/Common/utPolyTools.cpp
)
SET( IMPORTERS

View File

@ -0,0 +1,92 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "Common/PolyTools.h"
#include <assimp/defs.h>
using namespace Assimp;
class utPolyTools : public ::testing::Test {
// empty
};
TEST_F( utPolyTools, NewellNormalTest ) {
aiVector3t<ai_real> out;
static const size_t Num = 4;
static const size_t Capa = Num + 2;
ai_real x[Capa], y[Capa], z[Capa];
x[0] = 0;
x[1] = 1;
x[2] = 2;
x[3] = 3;
y[0] = 1;
y[1] = 2;
y[2] = 3;
y[3] = 4;
z[0] = z[1] = z[2] = z[3] = 0;
NewellNormal<3, 3, 3>(out, 4, x, y, z, Capa);
}
TEST_F(utPolyTools, DerivePlaneCoordinateSpaceTest) {
const aiVector3D vertices_ok[3] = {
aiVector3D(-1, -1, 0),
aiVector3D(0, 1, 0),
aiVector3D(1, -1, 0)
};
aiVector3D normal;
bool ok = true;
aiMatrix4x4 m_ok = DerivePlaneCoordinateSpace<ai_real>(vertices_ok, 3, ok, normal);
EXPECT_TRUE(ok);
EXPECT_FLOAT_EQ(normal.x, 0.0f);
EXPECT_FLOAT_EQ(normal.y, 0.0f);
EXPECT_FLOAT_EQ(normal.z, 1.0f);
const aiVector3D vertices_not_ok[3] = {
aiVector3D(-1, -1, 0),
aiVector3D(-1, -1, 0),
aiVector3D(-1, -1, 0)
};
aiMatrix4x4 m_not_ok = DerivePlaneCoordinateSpace<ai_real>(vertices_not_ok, 3, ok, normal);
EXPECT_FALSE(ok);
}

View File

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

View File

@ -49,8 +49,8 @@ using namespace Assimp;
class TriangulateProcessTest : public ::testing::Test {
public:
virtual void SetUp();
virtual void TearDown();
void SetUp() override;
void TearDown() override;
protected:
aiMesh *pcMesh;
@ -132,6 +132,6 @@ TEST_F(TriangulateProcessTest, testTriangulation) {
}
}
// we should have no valid normal vectors now necause we aren't a pure polygon mesh
// we should have no valid normal vectors now because we aren't a pure polygon mesh
EXPECT_TRUE(pcMesh->mNormals == NULL);
}

View File

@ -43,9 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AV_ANIMEVALUATOR_H_INCLUDED
#define AV_ANIMEVALUATOR_H_INCLUDED
#include <assimp/matrix4x4.h>
#include <tuple>
#include <vector>
struct aiAnimation;
namespace AssimpView {
/**
@ -74,7 +78,7 @@ public:
* the aiAnimation. */
const std::vector<aiMatrix4x4> &GetTransformations() const { return mTransforms; }
protected:
private:
const aiAnimation *mAnim;
double mLastTime;
std::vector<std::tuple<unsigned int, unsigned int, unsigned int>> mLastPositions;

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AV_SCENEANIMATOR_H_INCLUDED
#define AV_SCENEANIMATOR_H_INCLUDED
#include <assimp/scene.h>
#include <map>
#include <string>
namespace AssimpView {
@ -59,7 +62,7 @@ namespace AssimpView {
struct SceneAnimNode {
std::string mName;
SceneAnimNode *mParent;
std::vector<SceneAnimNode *> mChildren;
std::vector<SceneAnimNode*> mChildren;
//! most recently calculated local transform
aiMatrix4x4 mLocalTransform;
@ -72,13 +75,23 @@ struct SceneAnimNode {
//! Default construction
SceneAnimNode() :
mName(), mParent(nullptr), mChildren(), mLocalTransform(), mGlobalTransform(), mChannelIndex(-1) {
mName(),
mParent(nullptr),
mChildren(),
mLocalTransform(),
mGlobalTransform(),
mChannelIndex(-1) {
// empty
}
//! Construction from a given name
SceneAnimNode(const std::string &pName) :
mName(pName), mParent(nullptr), mChildren(), mLocalTransform(), mGlobalTransform(), mChannelIndex(-1) {
mName(pName),
mParent(nullptr),
mChildren(),
mLocalTransform(),
mGlobalTransform(),
mChannelIndex(-1) {
// empty
}
@ -125,7 +138,7 @@ public:
// ----------------------------------------------------------------------------
/** Calculates the node transformations for the scene. Call this to get
* uptodate results before calling one of the getters.
* up-to-date results before calling one of the getters.
* @param pTime Current time. Can be an arbitrary range.
*/
void Calculate(double pTime);
@ -136,7 +149,7 @@ public:
* The returned matrix is in the node's parent's local space, just like the
* original node's transformation matrix. If the node is not animated, the
* node's original transformation is returned so that you can safely use or
* assign it to the node itsself. If there is no node with the given name,
* assign it to the node itself. If there is no node with the given name,
* the identity matrix is returned. All transformations are updated whenever
* Calculate() is called.
* @param pNodeName Name of the node
@ -151,7 +164,7 @@ public:
* The returned matrix is in world space, which is the same coordinate space
* as the transformation of the scene's root node. If the node is not animated,
* the node's original transformation is returned so that you can safely use or
* assign it to the node itsself. If there is no node with the given name, the
* assign it to the node itself. If there is no node with the given name, the
* identity matrix is returned. All transformations are updated whenever
* Calculate() is called.
* @param pNodeName Name of the node
@ -190,7 +203,7 @@ public:
/** @brief Get the current animation or NULL
*/
aiAnimation *CurrentAnim() const {
return static_cast<unsigned int>(mCurrentAnimIndex) < mScene->mNumAnimations ? mScene->mAnimations[mCurrentAnimIndex] : NULL;
return static_cast<unsigned int>(mCurrentAnimIndex) < mScene->mNumAnimations ? mScene->mAnimations[mCurrentAnimIndex] : nullptr;
}
protected: