- manually merged C4D importer code from acgessler branch
- manually merged IFC bugfixes and improvements from schrompf branchpull/469/head
parent
7c38a33225
commit
b71ded1ad0
|
@ -183,6 +183,52 @@ ENDIF ( ASSIMP_BUILD_COMPILER STREQUAL "")
|
||||||
|
|
||||||
MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
|
MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
|
||||||
|
|
||||||
|
|
||||||
|
SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL
|
||||||
|
"Build the C4D importer, which relies on the non-free Melange SDK."
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
IF ( MSVC )
|
||||||
|
SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/includes")
|
||||||
|
|
||||||
|
# pick the correct prebuilt library
|
||||||
|
IF(MSVC11)
|
||||||
|
SET(C4D_LIB_POSTFIX "_2012md")
|
||||||
|
ELSEIF(MSVC10)
|
||||||
|
SET(C4D_LIB_POSTFIX "_2010md")
|
||||||
|
ELSEIF(MSVC90)
|
||||||
|
SET(C4D_LIB_POSTFIX "_2008md")
|
||||||
|
ELSE()
|
||||||
|
MESSAGE( FATAL_ERROR
|
||||||
|
"C4D is currently only supported with MSVC 9, 10, 11"
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(CMAKE_CL_64)
|
||||||
|
SET(C4D_LIB_ARCH_POSTFIX "_x64")
|
||||||
|
ELSE()
|
||||||
|
SET(C4D_LIB_ARCH_POSTFIX "")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/lib/WIN")
|
||||||
|
|
||||||
|
SET(C4D_DEBUG_LIBRARY "${C4D_LIB_BASE_PATH}/debug/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib")
|
||||||
|
SET(C4D_RELEASE_LIBRARY "${C4D_LIB_BASE_PATH}/release/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib")
|
||||||
|
|
||||||
|
# winsock and winmm are necessary dependencies of melange (this is undocumented, but true.)
|
||||||
|
SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib)
|
||||||
|
ELSE ()
|
||||||
|
MESSAGE( FATAL_ERROR
|
||||||
|
"C4D is currently only available on Windows with melange SDK installed in contrib/Melange"
|
||||||
|
)
|
||||||
|
ENDIF ( MSVC )
|
||||||
|
else (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
|
||||||
|
ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ADD_SUBDIRECTORY( code/ )
|
ADD_SUBDIRECTORY( code/ )
|
||||||
option ( ASSIMP_BUILD_ASSIMP_TOOLS
|
option ( ASSIMP_BUILD_ASSIMP_TOOLS
|
||||||
"If the supplementary tools for Assimp are built in addition to the library."
|
"If the supplementary tools for Assimp are built in addition to the library."
|
||||||
|
|
|
@ -0,0 +1,641 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2012, 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.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file C4DImporter.cpp
|
||||||
|
* @brief Implementation of the Cinema4D importer class.
|
||||||
|
*/
|
||||||
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
|
// no #ifdefing here, Cinema4D support is carried out in a branch of assimp
|
||||||
|
// where it is turned on in the CMake settings.
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# error C4D support is currently MSVC only
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "C4DImporter.h"
|
||||||
|
#include "TinyFormatter.h"
|
||||||
|
|
||||||
|
#if defined(_M_X64) || defined(__amd64__)
|
||||||
|
# define __C4D_64BIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __PC
|
||||||
|
#include "c4d_file.h"
|
||||||
|
#include "default_alien_overloads.h"
|
||||||
|
|
||||||
|
using namespace _melange_;
|
||||||
|
|
||||||
|
// overload this function and fill in your own unique data
|
||||||
|
void GetWriterInfo(LONG &id, String &appname)
|
||||||
|
{
|
||||||
|
id = 2424226;
|
||||||
|
appname = "Open Asset Import Library";
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
using namespace Assimp::Formatter;
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const aiImporterDesc desc = {
|
||||||
|
"Cinema4D Importer",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
aiImporterFlags_SupportBinaryFlavour,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"c4d"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
C4DImporter::C4DImporter()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
C4DImporter::~C4DImporter()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
|
||||||
|
{
|
||||||
|
const std::string& extension = GetExtension(pFile);
|
||||||
|
if (extension == "c4d") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((!extension.length() || checkSig) && pIOHandler) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
const aiImporterDesc* C4DImporter::GetInfo () const
|
||||||
|
{
|
||||||
|
return &desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void C4DImporter::SetupProperties(const Importer* /*pImp*/)
|
||||||
|
{
|
||||||
|
// nothing to be done for the moment
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Imports the given file into the given scene structure.
|
||||||
|
void C4DImporter::InternReadFile( const std::string& pFile,
|
||||||
|
aiScene* pScene, IOSystem* pIOHandler)
|
||||||
|
{
|
||||||
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
|
||||||
|
|
||||||
|
if( file.get() == NULL) {
|
||||||
|
ThrowException("failed to open file " + pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t file_size = file->FileSize();
|
||||||
|
|
||||||
|
std::vector<uint8_t> mBuffer(file_size);
|
||||||
|
file->Read(&mBuffer[0], 1, file_size);
|
||||||
|
|
||||||
|
Filename f;
|
||||||
|
f.SetMemoryReadMode(&mBuffer[0], file_size);
|
||||||
|
|
||||||
|
// open document first
|
||||||
|
BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
|
||||||
|
if(doc == NULL) {
|
||||||
|
ThrowException("failed to read document " + pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
pScene->mRootNode = new aiNode("<C4DRoot>");
|
||||||
|
|
||||||
|
// first convert all materials
|
||||||
|
ReadMaterials(doc->GetFirstMaterial());
|
||||||
|
|
||||||
|
// process C4D scenegraph recursively
|
||||||
|
try {
|
||||||
|
RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
|
||||||
|
}
|
||||||
|
catch(...) {
|
||||||
|
BOOST_FOREACH(aiMesh* mesh, meshes) {
|
||||||
|
delete mesh;
|
||||||
|
}
|
||||||
|
BaseDocument::Free(doc);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
BaseDocument::Free(doc);
|
||||||
|
|
||||||
|
// copy meshes over
|
||||||
|
pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
|
||||||
|
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
|
||||||
|
std::copy(meshes.begin(), meshes.end(), pScene->mMeshes);
|
||||||
|
|
||||||
|
// copy materials over, adding a default material if necessary
|
||||||
|
unsigned int mat_count = static_cast<unsigned int>(materials.size());
|
||||||
|
BOOST_FOREACH(aiMesh* mesh, meshes) {
|
||||||
|
ai_assert(mesh->mMaterialIndex <= mat_count);
|
||||||
|
if(mesh->mMaterialIndex >= mat_count) {
|
||||||
|
++mat_count;
|
||||||
|
|
||||||
|
ScopeGuard<aiMaterial> def_material(new aiMaterial());
|
||||||
|
const aiString name(AI_DEFAULT_MATERIAL_NAME);
|
||||||
|
def_material->AddProperty(&name, AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
materials.push_back(def_material.dismiss());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pScene->mNumMaterials = static_cast<unsigned int>(materials.size());
|
||||||
|
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
|
||||||
|
std::copy(materials.begin(), materials.end(), pScene->mMaterials);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
bool C4DImporter::ReadShader(aiMaterial* out, _melange_::BaseShader* shader)
|
||||||
|
{
|
||||||
|
// based on Melange sample code (C4DImportExport.cpp)
|
||||||
|
while(shader) {
|
||||||
|
if(shader->GetType() == Xlayer) {
|
||||||
|
BaseContainer* container = shader->GetDataInstance();
|
||||||
|
GeData blend = container->GetData(SLA_LAYER_BLEND);
|
||||||
|
iBlendDataType* blend_list = reinterpret_cast<iBlendDataType*>(blend.GetCustomDataType(CUSTOMDATA_BLEND_LIST));
|
||||||
|
if (!blend_list)
|
||||||
|
{
|
||||||
|
LogWarn("ignoring XLayer shader: no blend list given");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerShaderLayer *lsl = dynamic_cast<LayerShaderLayer*>(blend_list->m_BlendLayers.GetObject(0));
|
||||||
|
|
||||||
|
// Ignore the actual layer blending - models for real-time rendering should not
|
||||||
|
// use them in a non-trivial way. Just try to find textures that we can apply
|
||||||
|
// to the model.
|
||||||
|
while (lsl)
|
||||||
|
{
|
||||||
|
if (lsl->GetType() == TypeFolder)
|
||||||
|
{
|
||||||
|
BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
|
||||||
|
LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
|
||||||
|
|
||||||
|
while (subLsl)
|
||||||
|
{
|
||||||
|
if (subLsl->GetType() == TypeShader) {
|
||||||
|
BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
|
||||||
|
if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subLsl = subLsl->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lsl->GetType() == TypeShader) {
|
||||||
|
BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
|
||||||
|
if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lsl = lsl->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( shader->GetType() == Xbitmap )
|
||||||
|
{
|
||||||
|
aiString path;
|
||||||
|
shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
|
||||||
|
path.length = ::strlen(path.data);
|
||||||
|
out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
|
||||||
|
}
|
||||||
|
shader = shader->GetNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void C4DImporter::ReadMaterials(_melange_::BaseMaterial* mat)
|
||||||
|
{
|
||||||
|
// based on Melange sample code
|
||||||
|
while (mat)
|
||||||
|
{
|
||||||
|
const String& name = mat->GetName();
|
||||||
|
if (mat->GetType() == Mmaterial)
|
||||||
|
{
|
||||||
|
aiMaterial* out = new aiMaterial();
|
||||||
|
material_mapping[mat] = static_cast<unsigned int>(materials.size());
|
||||||
|
materials.push_back(out);
|
||||||
|
|
||||||
|
aiString ai_name;
|
||||||
|
name.GetCString(ai_name.data, MAXLEN-1);
|
||||||
|
ai_name.length = ::strlen(ai_name.data);
|
||||||
|
out->AddProperty(&ai_name, AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
Material& m = dynamic_cast<Material&>(*mat);
|
||||||
|
|
||||||
|
if (m.GetChannelState(CHANNEL_COLOR))
|
||||||
|
{
|
||||||
|
GeData data;
|
||||||
|
mat->GetParameter(MATERIAL_COLOR_COLOR, data);
|
||||||
|
Vector color = data.GetVector();
|
||||||
|
mat->GetParameter(MATERIAL_COLOR_BRIGHTNESS, data);
|
||||||
|
const Real brightness = data.GetReal();
|
||||||
|
|
||||||
|
color *= brightness;
|
||||||
|
|
||||||
|
aiVector3D v;
|
||||||
|
v.x = color.x;
|
||||||
|
v.y = color.y;
|
||||||
|
v.z = color.z;
|
||||||
|
out->AddProperty(&v, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseShader* const shader = m.GetShader(MATERIAL_COLOR_SHADER);
|
||||||
|
if(shader) {
|
||||||
|
ReadShader(out, shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
|
||||||
|
}
|
||||||
|
mat = mat->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
|
||||||
|
{
|
||||||
|
ai_assert(parent != NULL);
|
||||||
|
std::vector<aiNode*> nodes;
|
||||||
|
|
||||||
|
// based on Melange sample code
|
||||||
|
while (object)
|
||||||
|
{
|
||||||
|
const String& name = object->GetName();
|
||||||
|
const LONG type = object->GetType();
|
||||||
|
const Matrix& ml = object->GetMl();
|
||||||
|
|
||||||
|
aiString string;
|
||||||
|
name.GetCString(string.data, MAXLEN-1);
|
||||||
|
string.length = ::strlen(string.data);
|
||||||
|
aiNode* const nd = new aiNode();
|
||||||
|
|
||||||
|
nd->mParent = parent;
|
||||||
|
nd->mName = string;
|
||||||
|
|
||||||
|
nd->mTransformation.a1 = ml.v1.x;
|
||||||
|
nd->mTransformation.b1 = ml.v1.y;
|
||||||
|
nd->mTransformation.c1 = ml.v1.z;
|
||||||
|
|
||||||
|
nd->mTransformation.a2 = ml.v2.x;
|
||||||
|
nd->mTransformation.b2 = ml.v2.y;
|
||||||
|
nd->mTransformation.c2 = ml.v2.z;
|
||||||
|
|
||||||
|
nd->mTransformation.a3 = ml.v3.x;
|
||||||
|
nd->mTransformation.b3 = ml.v3.y;
|
||||||
|
nd->mTransformation.c3 = ml.v3.z;
|
||||||
|
|
||||||
|
nd->mTransformation.a4 = ml.off.x;
|
||||||
|
nd->mTransformation.b4 = ml.off.y;
|
||||||
|
nd->mTransformation.c4 = ml.off.z;
|
||||||
|
|
||||||
|
nodes.push_back(nd);
|
||||||
|
|
||||||
|
GeData data;
|
||||||
|
if (type == Ocamera)
|
||||||
|
{
|
||||||
|
object->GetParameter(CAMERAOBJECT_FOV, data);
|
||||||
|
// TODO: read camera
|
||||||
|
}
|
||||||
|
else if (type == Olight)
|
||||||
|
{
|
||||||
|
// TODO: read light
|
||||||
|
}
|
||||||
|
else if (type == Opolygon)
|
||||||
|
{
|
||||||
|
aiMesh* const mesh = ReadMesh(object);
|
||||||
|
if(mesh != NULL) {
|
||||||
|
nd->mNumMeshes = 1;
|
||||||
|
nd->mMeshes = new unsigned int[1];
|
||||||
|
nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
|
||||||
|
meshes.push_back(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
RecurseHierarchy(object->GetDown(), nd);
|
||||||
|
object = object->GetNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy nodes over to parent
|
||||||
|
parent->mNumChildren = static_cast<unsigned int>(nodes.size());
|
||||||
|
parent->mChildren = new aiNode*[parent->mNumChildren]();
|
||||||
|
std::copy(nodes.begin(), nodes.end(), parent->mChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
aiMesh* C4DImporter::ReadMesh(BaseObject* object)
|
||||||
|
{
|
||||||
|
assert(object != NULL && object->GetType() == Opolygon);
|
||||||
|
|
||||||
|
// based on Melange sample code
|
||||||
|
PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
|
||||||
|
ai_assert(polyObject != NULL);
|
||||||
|
|
||||||
|
const LONG pointCount = polyObject->GetPointCount();
|
||||||
|
const LONG polyCount = polyObject->GetPolygonCount();
|
||||||
|
if(!polyObject || !pointCount) {
|
||||||
|
LogWarn("ignoring mesh with zero vertices or faces");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector* points = polyObject->GetPointR();
|
||||||
|
ai_assert(points != NULL);
|
||||||
|
|
||||||
|
const CPolygon* polys = polyObject->GetPolygonR();
|
||||||
|
ai_assert(polys != NULL);
|
||||||
|
|
||||||
|
ScopeGuard<aiMesh> mesh(new aiMesh());
|
||||||
|
mesh->mNumFaces = static_cast<unsigned int>(polyCount);
|
||||||
|
aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces]();
|
||||||
|
|
||||||
|
mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
||||||
|
mesh->mMaterialIndex = 0;
|
||||||
|
|
||||||
|
unsigned int vcount = 0;
|
||||||
|
|
||||||
|
// first count vertices
|
||||||
|
for (LONG i = 0; i < polyCount; i++)
|
||||||
|
{
|
||||||
|
vcount += 3;
|
||||||
|
|
||||||
|
// TODO: do we also need to handle lines or points with similar checks?
|
||||||
|
if (polys[i].c != polys[i].d)
|
||||||
|
{
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
||||||
|
++vcount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ai_assert(vcount > 0);
|
||||||
|
|
||||||
|
mesh->mNumVertices = vcount;
|
||||||
|
aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
|
||||||
|
aiVector3D* normals, *uvs, *tangents, *bitangents;
|
||||||
|
unsigned int n = 0;
|
||||||
|
|
||||||
|
// check if there are normals, tangents or UVW coordinates
|
||||||
|
BaseTag* tag = object->GetTag(Tnormal);
|
||||||
|
NormalTag* normals_src = NULL;
|
||||||
|
if(tag) {
|
||||||
|
normals_src = dynamic_cast<NormalTag*>(tag);
|
||||||
|
normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
|
||||||
|
}
|
||||||
|
|
||||||
|
tag = object->GetTag(Ttangent);
|
||||||
|
TangentTag* tangents_src = NULL;
|
||||||
|
if(tag) {
|
||||||
|
tangents_src = dynamic_cast<TangentTag*>(tag);
|
||||||
|
tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
|
||||||
|
bitangents = mesh->mBitangents = new aiVector3D[mesh->mNumVertices]();
|
||||||
|
}
|
||||||
|
|
||||||
|
tag = object->GetTag(Tuvw);
|
||||||
|
UVWTag* uvs_src = NULL;
|
||||||
|
if(tag) {
|
||||||
|
uvs_src = dynamic_cast<UVWTag*>(tag);
|
||||||
|
uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy vertices and extra channels over and populate faces
|
||||||
|
for (LONG i = 0; i < polyCount; ++i, ++face)
|
||||||
|
{
|
||||||
|
ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
|
||||||
|
const Vector& pointA = points[polys[i].a];
|
||||||
|
verts->x = pointA.x;
|
||||||
|
verts->y = pointA.y;
|
||||||
|
verts->z = pointA.z;
|
||||||
|
++verts;
|
||||||
|
|
||||||
|
ai_assert(polys[i].b < pointCount && polys[i].b >= 0);
|
||||||
|
const Vector& pointB = points[polys[i].b];
|
||||||
|
verts->x = pointB.x;
|
||||||
|
verts->y = pointB.y;
|
||||||
|
verts->z = pointB.z;
|
||||||
|
++verts;
|
||||||
|
|
||||||
|
ai_assert(polys[i].c < pointCount && polys[i].c >= 0);
|
||||||
|
const Vector& pointC = points[polys[i].c];
|
||||||
|
verts->x = pointC.x;
|
||||||
|
verts->y = pointC.y;
|
||||||
|
verts->z = pointC.z;
|
||||||
|
++verts;
|
||||||
|
|
||||||
|
// TODO: do we also need to handle lines or points with similar checks?
|
||||||
|
if (polys[i].c != polys[i].d)
|
||||||
|
{
|
||||||
|
ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
|
||||||
|
|
||||||
|
face->mNumIndices = 4;
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
||||||
|
const Vector& pointD = points[polys[i].d];
|
||||||
|
verts->x = pointD.x;
|
||||||
|
verts->y = pointD.y;
|
||||||
|
verts->z = pointD.z;
|
||||||
|
++verts;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
face->mNumIndices = 3;
|
||||||
|
}
|
||||||
|
face->mIndices = new unsigned int[face->mNumIndices];
|
||||||
|
for(unsigned int j = 0; j < face->mNumIndices; ++j) {
|
||||||
|
face->mIndices[j] = n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy normals
|
||||||
|
if (normals_src) {
|
||||||
|
if(i >= normals_src->GetNormalCount()) {
|
||||||
|
LogError("unexpected number of normals, ignoring");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const NormalStruct& nor = normals_src->GetNormals(i);
|
||||||
|
normals->x = nor.a.x;
|
||||||
|
normals->y = nor.a.y;
|
||||||
|
normals->z = nor.a.z;
|
||||||
|
++normals;
|
||||||
|
|
||||||
|
normals->x = nor.b.x;
|
||||||
|
normals->y = nor.b.y;
|
||||||
|
normals->z = nor.b.z;
|
||||||
|
++normals;
|
||||||
|
|
||||||
|
normals->x = nor.c.x;
|
||||||
|
normals->y = nor.c.y;
|
||||||
|
normals->z = nor.c.z;
|
||||||
|
++normals;
|
||||||
|
|
||||||
|
if(face->mNumIndices == 4) {
|
||||||
|
normals->x = nor.d.x;
|
||||||
|
normals->y = nor.d.y;
|
||||||
|
normals->z = nor.d.z;
|
||||||
|
++normals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy tangents and bitangents
|
||||||
|
if (tangents_src) {
|
||||||
|
|
||||||
|
for(unsigned int k = 0; k < face->mNumIndices; ++k) {
|
||||||
|
LONG l;
|
||||||
|
switch(k) {
|
||||||
|
case 0:
|
||||||
|
l = polys[i].a;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
l = polys[i].b;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
l = polys[i].c;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
l = polys[i].d;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ai_assert(false);
|
||||||
|
}
|
||||||
|
if(l >= tangents_src->GetDataCount()) {
|
||||||
|
LogError("unexpected number of tangents, ignoring");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tangent tan = tangents_src->GetDataR()[l];
|
||||||
|
tangents->x = tan.vl.x;
|
||||||
|
tangents->y = tan.vl.y;
|
||||||
|
tangents->z = tan.vl.z;
|
||||||
|
++tangents;
|
||||||
|
|
||||||
|
bitangents->x = tan.vr.x;
|
||||||
|
bitangents->y = tan.vr.y;
|
||||||
|
bitangents->z = tan.vr.z;
|
||||||
|
++bitangents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy UVs
|
||||||
|
if (uvs_src) {
|
||||||
|
if(i >= uvs_src->GetDataCount()) {
|
||||||
|
LogError("unexpected number of UV coordinates, ignoring");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UVWStruct uvw;
|
||||||
|
uvs_src->Get(uvs_src->GetDataAddressR(),i,uvw);
|
||||||
|
|
||||||
|
uvs->x = uvw.a.x;
|
||||||
|
uvs->y = 1.0f-uvw.a.y;
|
||||||
|
uvs->z = uvw.a.z;
|
||||||
|
++uvs;
|
||||||
|
|
||||||
|
uvs->x = uvw.b.x;
|
||||||
|
uvs->y = 1.0f-uvw.b.y;
|
||||||
|
uvs->z = uvw.b.z;
|
||||||
|
++uvs;
|
||||||
|
|
||||||
|
uvs->x = uvw.c.x;
|
||||||
|
uvs->y = 1.0f-uvw.c.y;
|
||||||
|
uvs->z = uvw.c.z;
|
||||||
|
++uvs;
|
||||||
|
|
||||||
|
if(face->mNumIndices == 4) {
|
||||||
|
uvs->x = uvw.d.x;
|
||||||
|
uvs->y = 1.0f-uvw.d.y;
|
||||||
|
uvs->z = uvw.d.z;
|
||||||
|
++uvs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh->mMaterialIndex = ResolveMaterial(polyObject);
|
||||||
|
return mesh.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
|
||||||
|
{
|
||||||
|
ai_assert(obj != NULL);
|
||||||
|
|
||||||
|
const unsigned int mat_count = static_cast<unsigned int>(materials.size());
|
||||||
|
|
||||||
|
BaseTag* tag = obj->GetTag(Ttexture);
|
||||||
|
if(tag == NULL) {
|
||||||
|
return mat_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
|
||||||
|
|
||||||
|
BaseMaterial* const mat = ttag.GetMaterial();
|
||||||
|
assert(mat != NULL);
|
||||||
|
|
||||||
|
const MaterialMap::const_iterator it = material_mapping.find(mat);
|
||||||
|
if(it == material_mapping.end()) {
|
||||||
|
return mat_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai_assert((*it).second < mat_count);
|
||||||
|
return (*it).second;
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2012, 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.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file C4DImporter.h
|
||||||
|
* @brief Declaration of the Cinema4D (*.c4d) importer class.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_AI_CINEMA_4D_LOADER_H
|
||||||
|
#define INCLUDED_AI_CINEMA_4D_LOADER_H
|
||||||
|
|
||||||
|
#include "BaseImporter.h"
|
||||||
|
#include "LogAux.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
struct aiImporterDesc;
|
||||||
|
|
||||||
|
namespace _melange_ {
|
||||||
|
class BaseObject; // c4d_file.h
|
||||||
|
class PolygonObject;
|
||||||
|
class BaseMaterial;
|
||||||
|
class BaseShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
// TinyFormatter.h
|
||||||
|
namespace Formatter {
|
||||||
|
template <typename T,typename TR, typename A> class basic_formatter;
|
||||||
|
typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
/** Importer class to load Cinema4D files using the Melange library to be obtained from
|
||||||
|
* www.plugincafe.com
|
||||||
|
*
|
||||||
|
* Note that Melange is not free software. */
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
C4DImporter();
|
||||||
|
~C4DImporter();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void ReadMaterials(_melange_::BaseMaterial* mat);
|
||||||
|
void RecurseHierarchy(_melange_::BaseObject* object, aiNode* parent);
|
||||||
|
aiMesh* ReadMesh(_melange_::BaseObject* object);
|
||||||
|
unsigned int ResolveMaterial(_melange_::PolygonObject* obj);
|
||||||
|
|
||||||
|
bool ReadShader(aiMaterial* out, _melange_::BaseShader* shader);
|
||||||
|
|
||||||
|
std::vector<aiMesh*> meshes;
|
||||||
|
std::vector<aiMaterial*> materials;
|
||||||
|
|
||||||
|
typedef std::map<_melange_::BaseMaterial*, unsigned int> MaterialMap;
|
||||||
|
MaterialMap material_mapping;
|
||||||
|
|
||||||
|
}; // !class C4DImporter
|
||||||
|
|
||||||
|
} // end of namespace Assimp
|
||||||
|
#endif // INCLUDED_AI_CINEMA_4D_LOADER_H
|
||||||
|
|
|
@ -148,6 +148,14 @@ SET( Common_SRCS
|
||||||
)
|
)
|
||||||
SOURCE_GROUP(Common FILES ${Common_SRCS})
|
SOURCE_GROUP(Common FILES ${Common_SRCS})
|
||||||
|
|
||||||
|
IF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
|
||||||
|
SET( C4D_SRCS
|
||||||
|
C4DImporter.cpp
|
||||||
|
C4DImporter.h
|
||||||
|
)
|
||||||
|
SOURCE_GROUP( C4D FILES ${C4D_SRCS})
|
||||||
|
ENDIF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
|
||||||
|
|
||||||
SET( 3DS_SRCS
|
SET( 3DS_SRCS
|
||||||
3DSConverter.cpp
|
3DSConverter.cpp
|
||||||
3DSHelper.h
|
3DSHelper.h
|
||||||
|
@ -718,6 +726,11 @@ SET( assimp_src
|
||||||
AssimpPCH.cpp
|
AssimpPCH.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
SET( assimp_src ${assimp_src} ${C4D_SRCS})
|
||||||
|
INCLUDE_DIRECTORIES(${C4D_INCLUDES})
|
||||||
|
ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
|
||||||
#ADD_MSVC_PRECOMPILED_HEADER("AssimpPCH.h" "AssimpPCH.cpp" assimp_src)
|
#ADD_MSVC_PRECOMPILED_HEADER("AssimpPCH.h" "AssimpPCH.cpp" assimp_src)
|
||||||
|
|
||||||
ADD_LIBRARY( assimp ${assimp_src} )
|
ADD_LIBRARY( assimp ${assimp_src} )
|
||||||
|
@ -730,6 +743,12 @@ if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
|
||||||
target_link_libraries(assimp android_jniiosystem)
|
target_link_libraries(assimp android_jniiosystem)
|
||||||
endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
|
endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
|
||||||
|
|
||||||
|
IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
TARGET_LINK_LIBRARIES(assimp optimized ${C4D_RELEASE_LIBRARY})
|
||||||
|
TARGET_LINK_LIBRARIES(assimp debug ${C4D_DEBUG_LIBRARY})
|
||||||
|
TARGET_LINK_LIBRARIES(assimp ${C4D_EXTRA_LIBRARIES})
|
||||||
|
ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
|
||||||
|
|
||||||
if( MSVC )
|
if( MSVC )
|
||||||
# in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
|
# in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
|
||||||
if( MSVC70 OR MSVC71 )
|
if( MSVC70 OR MSVC71 )
|
||||||
|
|
|
@ -50,42 +50,97 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "ProcessHelper.h"
|
#include "ProcessHelper.h"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace IFC {
|
namespace IFC {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
enum Intersect {
|
// Calculates intersection between line segment and plane. To catch corner cases, specify which side you prefer.
|
||||||
Intersect_No,
|
// The function then generates a hit only if the end is beyond a certain margin in that direction, filtering out
|
||||||
Intersect_LiesOnPlane,
|
// "very close to plane" ghost hits as long as start and end stay directly on or within the given plane side.
|
||||||
Intersect_Yes
|
bool IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0,
|
||||||
};
|
const IfcVector3& e1, bool assumeStartOnWhiteSide, IfcVector3& out)
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0,
|
|
||||||
const IfcVector3& e1,
|
|
||||||
IfcVector3& out)
|
|
||||||
{
|
{
|
||||||
const IfcVector3 pdelta = e0 - p, seg = e1-e0;
|
const IfcVector3 pdelta = e0 - p, seg = e1 - e0;
|
||||||
const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
|
const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
|
||||||
|
|
||||||
if (std::fabs(dotOne) < 1e-6) {
|
// if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this
|
||||||
return std::fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
|
// point leaves the plane through the other side
|
||||||
|
if( std::abs(dotOne + dotTwo) < 1e-6 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if segment starts on the plane, report a hit only if the end lies on the *other* side
|
||||||
|
if( std::abs(dotTwo) < 1e-6 )
|
||||||
|
{
|
||||||
|
if( (assumeStartOnWhiteSide && dotOne + dotTwo < 1e-6) || (!assumeStartOnWhiteSide && dotOne + dotTwo > -1e-6) )
|
||||||
|
{
|
||||||
|
out = e0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const IfcFloat t = dotTwo/dotOne;
|
// ignore if segment is parallel to plane and far away from it on either side
|
||||||
|
// Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered
|
||||||
|
if( std::abs(dotOne) < 1e-6 )
|
||||||
|
return false;
|
||||||
|
|
||||||
// t must be in [0..1] if the intersection point is within the given segment
|
// t must be in [0..1] if the intersection point is within the given segment
|
||||||
if (t > 1.f || t < 0.f) {
|
const IfcFloat t = dotTwo / dotOne;
|
||||||
return Intersect_No;
|
if( t > 1.0 || t < 0.0 )
|
||||||
}
|
return false;
|
||||||
out = e0+t*seg;
|
|
||||||
return Intersect_Yes;
|
out = e0 + t*seg;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void FilterPolygon(std::vector<IfcVector3>& resultpoly)
|
||||||
|
{
|
||||||
|
if( resultpoly.size() < 3 )
|
||||||
|
{
|
||||||
|
resultpoly.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IfcVector3 vmin, vmax;
|
||||||
|
ArrayBounds(resultpoly.data(), resultpoly.size(), vmin, vmax);
|
||||||
|
|
||||||
|
// filter our IfcFloat points - those may happen if a point lies
|
||||||
|
// directly on the intersection line or directly on the clipping plane
|
||||||
|
const IfcFloat epsilon = (vmax - vmin).SquareLength() / 1e6f;
|
||||||
|
FuzzyVectorCompare fz(epsilon);
|
||||||
|
std::vector<IfcVector3>::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz);
|
||||||
|
|
||||||
|
if( e != resultpoly.end() )
|
||||||
|
resultpoly.erase(e, resultpoly.end());
|
||||||
|
|
||||||
|
if( !resultpoly.empty() && fz(resultpoly.front(), resultpoly.back()) )
|
||||||
|
resultpoly.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void WritePolygon(std::vector<IfcVector3>& resultpoly, TempMesh& result)
|
||||||
|
{
|
||||||
|
FilterPolygon(resultpoly);
|
||||||
|
|
||||||
|
if( resultpoly.size() > 2 )
|
||||||
|
{
|
||||||
|
result.verts.insert(result.verts.end(), resultpoly.begin(), resultpoly.end());
|
||||||
|
result.vertcnt.push_back(resultpoly.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
|
void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
|
||||||
const TempMesh& first_operand,
|
const TempMesh& first_operand,
|
||||||
ConversionData& /*conv*/)
|
ConversionData& /*conv*/)
|
||||||
{
|
{
|
||||||
ai_assert(hs != NULL);
|
ai_assert(hs != NULL);
|
||||||
|
|
||||||
|
@ -120,20 +175,14 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
||||||
for(iit = begin; iit != end; vidx += *iit++) {
|
for(iit = begin; iit != end; vidx += *iit++) {
|
||||||
|
|
||||||
unsigned int newcount = 0;
|
unsigned int newcount = 0;
|
||||||
for(unsigned int i = 0; i < *iit; ++i) {
|
bool isAtWhiteSide = (in[vidx] - p) * n > -1e-6;
|
||||||
const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
|
for( unsigned int i = 0; i < *iit; ++i ) {
|
||||||
|
const IfcVector3& e0 = in[vidx + i], e1 = in[vidx + (i + 1) % *iit];
|
||||||
|
|
||||||
// does the next segment intersect the plane?
|
// does the next segment intersect the plane?
|
||||||
IfcVector3 isectpos;
|
IfcVector3 isectpos;
|
||||||
const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
|
if( IntersectSegmentPlane(p, n, e0, e1, isAtWhiteSide, isectpos) ) {
|
||||||
if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
|
if( isAtWhiteSide ) {
|
||||||
if ( (e0-p).Normalize()*n > 0 ) {
|
|
||||||
outvert.push_back(e0);
|
|
||||||
++newcount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isect == Intersect_Yes) {
|
|
||||||
if ( (e0-p).Normalize()*n > 0 ) {
|
|
||||||
// e0 is on the right side, so keep it
|
// e0 is on the right side, so keep it
|
||||||
outvert.push_back(e0);
|
outvert.push_back(e0);
|
||||||
outvert.push_back(isectpos);
|
outvert.push_back(isectpos);
|
||||||
|
@ -144,6 +193,14 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
||||||
outvert.push_back(isectpos);
|
outvert.push_back(isectpos);
|
||||||
++newcount;
|
++newcount;
|
||||||
}
|
}
|
||||||
|
isAtWhiteSide = !isAtWhiteSide;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( isAtWhiteSide ) {
|
||||||
|
outvert.push_back(e0);
|
||||||
|
++newcount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,76 +242,114 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Check if e0-e1 intersects a sub-segment of the given boundary line.
|
// Check if e0-e1 intersects a sub-segment of the given boundary line.
|
||||||
// note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
|
// note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
|
||||||
bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
|
// New version takes the supposed inside/outside state as a parameter and treats corner cases as if
|
||||||
std::vector<size_t>& intersected_boundary_segments,
|
// the line stays on that side. This should make corner cases more stable.
|
||||||
std::vector<IfcVector3>& intersected_boundary_points,
|
// Two million assumptions! Boundary should have all z at 0.0, will be treated as closed, should not have
|
||||||
bool half_open = false,
|
// segments with length <1e-6, self-intersecting might break the corner case handling... just don't go there, ok?
|
||||||
bool* e0_hits_border = NULL)
|
bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
|
||||||
|
const bool isStartAssumedInside, std::vector<std::pair<size_t, IfcVector3> >& intersect_results,
|
||||||
|
const bool halfOpen = false)
|
||||||
{
|
{
|
||||||
ai_assert(intersected_boundary_segments.empty());
|
ai_assert(intersect_results.empty());
|
||||||
ai_assert(intersected_boundary_points.empty());
|
|
||||||
|
|
||||||
if(e0_hits_border) {
|
// determine winding order - necessary to detect segments going "inwards" or "outwards" from a point directly on the border
|
||||||
*e0_hits_border = false;
|
// positive sum of angles means clockwise order when looking down the -Z axis
|
||||||
|
IfcFloat windingOrder = 0.0;
|
||||||
|
for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
|
||||||
|
IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i];
|
||||||
|
IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount];
|
||||||
|
IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane
|
||||||
|
// Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation
|
||||||
|
// could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side))
|
||||||
|
windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y);
|
||||||
}
|
}
|
||||||
|
windingOrder = windingOrder > 0.0 ? 1.0 : -1.0;
|
||||||
|
|
||||||
const IfcVector3& e = e1 - e0;
|
const IfcVector3 e = e1 - e0;
|
||||||
|
|
||||||
for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) {
|
for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
|
||||||
// boundary segment i: b0-b1
|
// boundary segment i: b0-b1
|
||||||
const IfcVector3& b0 = boundary[i];
|
const IfcVector3& b0 = boundary[i];
|
||||||
const IfcVector3& b1 = boundary[(i+1) % bcount];
|
const IfcVector3& b1 = boundary[(i + 1) % bcount];
|
||||||
|
IfcVector3 b = b1 - b0;
|
||||||
const IfcVector3& b = b1 - b0;
|
IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
|
||||||
|
|
||||||
// segment-segment intersection
|
// segment-segment intersection
|
||||||
// solve b0 + b*s = e0 + e*t for (s,t)
|
// solve b0 + b*s = e0 + e*t for (s,t)
|
||||||
const IfcFloat det = (-b.x * e.y + e.x * b.y);
|
const IfcFloat det = (-b.x * e.y + e.x * b.y);
|
||||||
if(std::fabs(det) < 1e-6) {
|
if( std::abs(det) < 1e-6 ) {
|
||||||
// no solutions (parallel lines)
|
// no solutions (parallel lines)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IfcFloat x = b0.x - e0.x;
|
const IfcFloat x = b0.x - e0.x;
|
||||||
const IfcFloat y = b0.y - e0.y;
|
const IfcFloat y = b0.y - e0.y;
|
||||||
|
const IfcFloat s = (x*e.y - e.x*y) / det; // scale along boundary edge
|
||||||
const IfcFloat s = (x*e.y - e.x*y)/det;
|
const IfcFloat t = (x*b.y - b.x*y) / det; // scale along given segment
|
||||||
const IfcFloat t = (x*b.y - b.x*y)/det;
|
const IfcVector3 p = e0 + e*t;
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_DEBUG
|
#ifdef ASSIMP_BUILD_DEBUG
|
||||||
const IfcVector3 check = b0 + b*s - (e0 + e*t);
|
const IfcVector3 check = b0 + b*s - p;
|
||||||
ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5);
|
ai_assert((IfcVector2(check.x, check.y)).SquareLength() < 1e-5);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// for a valid intersection, s-t should be in range [0,1].
|
// also calculate the distance of e0 and e1 to the segment. We need to detect the "starts directly on segment"
|
||||||
// note that for t (i.e. the segment point) we only use a
|
// and "ends directly at segment" cases
|
||||||
// half-sided epsilon because the next segment should catch
|
bool startsAtSegment, endsAtSegment;
|
||||||
// this case.
|
{
|
||||||
const IfcFloat epsilon = 1e-6;
|
// calculate closest point to each end on the segment, clamp that point to the segment's length, then check
|
||||||
if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
|
// distance to that point. This approach is like testing if e0 is inside a capped cylinder.
|
||||||
|
IfcFloat et0 = (b.x*(e0.x - b0.x) + b.y*(e0.y - b0.y)) * b_sqlen_inv;
|
||||||
|
IfcVector3 closestPosToE0OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et0)) * b;
|
||||||
|
startsAtSegment = (closestPosToE0OnBoundary - IfcVector3(e0.x, e0.y, 0.0)).SquareLength() < 1e-12;
|
||||||
|
IfcFloat et1 = (b.x*(e1.x - b0.x) + b.y*(e1.y - b0.y)) * b_sqlen_inv;
|
||||||
|
IfcVector3 closestPosToE1OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et1)) * b;
|
||||||
|
endsAtSegment = (closestPosToE1OnBoundary - IfcVector3(e1.x, e1.y, 0.0)).SquareLength() < 1e-12;
|
||||||
|
}
|
||||||
|
|
||||||
if (e0_hits_border && !*e0_hits_border) {
|
// Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments
|
||||||
*e0_hits_border = std::fabs(t) < 1e-5f;
|
if( endsAtSegment && !halfOpen )
|
||||||
}
|
continue;
|
||||||
|
|
||||||
const IfcVector3& p = e0 + e*t;
|
// Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE
|
||||||
|
// state. This should catch the case where a connected set of segments has a point directly on the boundary,
|
||||||
|
// one segment not hitting it because it ends there and the next segment not hitting it because it starts there
|
||||||
|
// Should NOT generate a hit if the segment only touches the boundary but turns around and stays inside.
|
||||||
|
if( startsAtSegment )
|
||||||
|
{
|
||||||
|
IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder;
|
||||||
|
bool isGoingInside = (inside_dir * e) > 0.0;
|
||||||
|
if( isGoingInside == isStartAssumedInside )
|
||||||
|
continue;
|
||||||
|
|
||||||
// only insert the point into the list if it is sufficiently
|
// only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
||||||
// far away from the previous intersection point. This way,
|
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
||||||
// we avoid duplicate detection if the intersection is
|
if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
|
||||||
// directly on the vertex between two segments.
|
{
|
||||||
if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) {
|
const IfcVector3 diff = intersect_results.back().second - e0;
|
||||||
const IfcVector3 diff = intersected_boundary_points.back() - p;
|
if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
|
||||||
if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
intersected_boundary_segments.push_back(i);
|
intersect_results.push_back(std::make_pair(i, e0));
|
||||||
intersected_boundary_points.push_back(p);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for a valid intersection, s and t should be in range [0,1]. Including a bit of epsilon on s, potential double
|
||||||
|
// hits on two consecutive boundary segments are filtered
|
||||||
|
if( s >= -1e-6 * b_sqlen_inv && s <= 1.0 + 1e-6*b_sqlen_inv && t >= 0.0 && (t <= 1.0 || halfOpen) )
|
||||||
|
{
|
||||||
|
// only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
||||||
|
// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
||||||
|
if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
|
||||||
|
{
|
||||||
|
const IfcVector3 diff = intersect_results.back().second - p;
|
||||||
|
if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
intersect_results.push_back(std::make_pair(i, p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !intersected_boundary_segments.empty();
|
return !intersect_results.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,47 +367,21 @@ bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
|
||||||
// the border of the polygon. If any of our attempts produces this result,
|
// the border of the polygon. If any of our attempts produces this result,
|
||||||
// we return false immediately.
|
// we return false immediately.
|
||||||
|
|
||||||
std::vector<size_t> intersected_boundary_segments;
|
std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
|
||||||
std::vector<IfcVector3> intersected_boundary_points;
|
|
||||||
size_t votes = 0;
|
size_t votes = 0;
|
||||||
|
|
||||||
bool is_border;
|
IntersectsBoundaryProfile(p, p + IfcVector3(1.0, 0, 0), boundary, true, intersected_boundary, true);
|
||||||
IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary,
|
votes += intersected_boundary.size() % 2;
|
||||||
intersected_boundary_segments,
|
|
||||||
intersected_boundary_points, true, &is_border);
|
|
||||||
|
|
||||||
if(is_border) {
|
intersected_boundary.clear();
|
||||||
return false;
|
IntersectsBoundaryProfile(p, p + IfcVector3(0, 1.0, 0), boundary, true, intersected_boundary, true);
|
||||||
}
|
votes += intersected_boundary.size() % 2;
|
||||||
|
|
||||||
votes += intersected_boundary_segments.size() % 2;
|
intersected_boundary.clear();
|
||||||
|
IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true);
|
||||||
|
votes += intersected_boundary.size() % 2;
|
||||||
|
|
||||||
intersected_boundary_segments.clear();
|
ai_assert(votes == 3 || votes == 0);
|
||||||
intersected_boundary_points.clear();
|
|
||||||
|
|
||||||
IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary,
|
|
||||||
intersected_boundary_segments,
|
|
||||||
intersected_boundary_points, true, &is_border);
|
|
||||||
|
|
||||||
if(is_border) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
votes += intersected_boundary_segments.size() % 2;
|
|
||||||
|
|
||||||
intersected_boundary_segments.clear();
|
|
||||||
intersected_boundary_points.clear();
|
|
||||||
|
|
||||||
IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary,
|
|
||||||
intersected_boundary_segments,
|
|
||||||
intersected_boundary_points, true, &is_border);
|
|
||||||
|
|
||||||
if(is_border) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
votes += intersected_boundary_segments.size() % 2;
|
|
||||||
//ai_assert(votes == 3 || votes == 0);
|
|
||||||
return votes > 1;
|
return votes > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,6 +419,9 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determine winding order by calculating the normal.
|
||||||
|
IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(profile->verts.data(), profile->verts.size());
|
||||||
|
|
||||||
IfcMatrix4 proj_inv;
|
IfcMatrix4 proj_inv;
|
||||||
ConvertAxisPlacement(proj_inv,hs->Position);
|
ConvertAxisPlacement(proj_inv,hs->Position);
|
||||||
|
|
||||||
|
@ -361,256 +433,283 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
|
||||||
// clip the current contents of `meshout` against the plane we obtained from the second operand
|
// clip the current contents of `meshout` against the plane we obtained from the second operand
|
||||||
const std::vector<IfcVector3>& in = first_operand.verts;
|
const std::vector<IfcVector3>& in = first_operand.verts;
|
||||||
std::vector<IfcVector3>& outvert = result.verts;
|
std::vector<IfcVector3>& outvert = result.verts;
|
||||||
|
std::vector<unsigned int>& outvertcnt = result.vertcnt;
|
||||||
std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(),
|
|
||||||
end = first_operand.vertcnt.end(), iit;
|
|
||||||
|
|
||||||
outvert.reserve(in.size());
|
outvert.reserve(in.size());
|
||||||
result.vertcnt.reserve(first_operand.vertcnt.size());
|
outvertcnt.reserve(first_operand.vertcnt.size());
|
||||||
|
|
||||||
std::vector<size_t> intersected_boundary_segments;
|
|
||||||
std::vector<IfcVector3> intersected_boundary_points;
|
|
||||||
|
|
||||||
// TODO: the following algorithm doesn't handle all cases.
|
|
||||||
unsigned int vidx = 0;
|
unsigned int vidx = 0;
|
||||||
for(iit = begin; iit != end; vidx += *iit++) {
|
std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin();
|
||||||
if (!*iit) {
|
std::vector<unsigned int>::const_iterator end = first_operand.vertcnt.end();
|
||||||
continue;
|
std::vector<unsigned int>::const_iterator iit;
|
||||||
|
for( iit = begin; iit != end; vidx += *iit++ )
|
||||||
|
{
|
||||||
|
// Our new approach: we cut the poly along the plane, then we intersect the part on the black side of the plane
|
||||||
|
// against the bounding polygon. All the white parts, and the black part outside the boundary polygon, are kept.
|
||||||
|
std::vector<IfcVector3> whiteside, blackside;
|
||||||
|
|
||||||
|
{
|
||||||
|
const IfcVector3* srcVertices = &in[vidx];
|
||||||
|
const size_t srcVtxCount = *iit;
|
||||||
|
if( srcVtxCount == 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
IfcVector3 polyNormal = TempMesh::ComputePolygonNormal(srcVertices, srcVtxCount, true);
|
||||||
|
polyNormal = IfcMatrix3(proj) * polyNormal;
|
||||||
|
|
||||||
|
// if the poly is parallel to the plane, put it completely on the black or white side
|
||||||
|
if( std::abs(polyNormal * n) > 0.9999 )
|
||||||
|
{
|
||||||
|
bool isOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6;
|
||||||
|
std::vector<IfcVector3>& targetSide = isOnWhiteSide ? whiteside : blackside;
|
||||||
|
targetSide.insert(targetSide.end(), srcVertices, srcVertices + srcVtxCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// otherwise start building one polygon for each side. Whenever the current line segment intersects the plane
|
||||||
|
// we put a point there as an end of the current segment. Then we switch to the other side, put a point there, too,
|
||||||
|
// as a beginning of the current segment, and simply continue accumulating vertices.
|
||||||
|
bool isCurrentlyOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6;
|
||||||
|
for( size_t a = 0; a < srcVtxCount; ++a )
|
||||||
|
{
|
||||||
|
IfcVector3 e0 = proj * srcVertices[a];
|
||||||
|
IfcVector3 e1 = proj * srcVertices[(a + 1) % srcVtxCount];
|
||||||
|
IfcVector3 ei;
|
||||||
|
|
||||||
|
// put starting point to the current mesh
|
||||||
|
std::vector<IfcVector3>& trgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
|
||||||
|
trgt.push_back(srcVertices[a]);
|
||||||
|
|
||||||
|
// if there's an intersection, put an end vertex there, switch to the other side's mesh,
|
||||||
|
// and add a starting vertex there, too
|
||||||
|
bool isPlaneHit = IntersectSegmentPlane(p, n, e0, e1, isCurrentlyOnWhiteSide, ei);
|
||||||
|
if( isPlaneHit )
|
||||||
|
{
|
||||||
|
IfcVector3 global_ei = proj_inv * ei;
|
||||||
|
if( trgt.empty() || (trgt.back() - global_ei).SquareLength() > 1e-12 )
|
||||||
|
trgt.push_back(global_ei);
|
||||||
|
isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide;
|
||||||
|
std::vector<IfcVector3>& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
|
||||||
|
newtrgt.push_back(global_ei);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int newcount = 0;
|
// the part on the white side can be written into the target mesh right away
|
||||||
bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts);
|
WritePolygon(whiteside, result);
|
||||||
|
|
||||||
// used any more?
|
// The black part is the piece we need to get rid of, but only the part of it within the boundary polygon.
|
||||||
//size_t last_intersected_boundary_segment;
|
// So we now need to construct all the polygons that result from BlackSidePoly minus BoundaryPoly.
|
||||||
IfcVector3 last_intersected_boundary_point;
|
FilterPolygon(blackside);
|
||||||
|
|
||||||
bool extra_point_flag = false;
|
// Complicated, II. We run along the polygon. a) When we're inside the boundary, we run on until we hit an
|
||||||
IfcVector3 extra_point;
|
// intersection, which means we're leaving it. We then start a new out poly there. b) When we're outside the
|
||||||
|
// boundary, we start collecting vertices until we hit an intersection, then we run along the boundary until we hit
|
||||||
|
// an intersection, then we switch back to the poly and run on on this one again, and so on until we got a closed
|
||||||
|
// loop. Then we continue with the path we left to catch potential additional polys on the other side of the
|
||||||
|
// boundary as described in a)
|
||||||
|
if( !blackside.empty() )
|
||||||
|
{
|
||||||
|
// poly edge index, intersection point, edge index in boundary poly
|
||||||
|
std::vector<boost::tuple<size_t, IfcVector3, size_t> > intersections;
|
||||||
|
bool startedInside = PointInPoly(proj * blackside.front(), profile->verts);
|
||||||
|
bool isCurrentlyInside = startedInside;
|
||||||
|
|
||||||
IfcVector3 enter_volume;
|
std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
|
||||||
bool entered_volume_flag = false;
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < *iit; ++i) {
|
for( size_t a = 0; a < blackside.size(); ++a )
|
||||||
// current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set
|
{
|
||||||
const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i];
|
const IfcVector3 e0 = proj * blackside[a];
|
||||||
const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit];
|
const IfcVector3 e1 = proj * blackside[(a + 1) % blackside.size()];
|
||||||
|
|
||||||
// does the current segment intersect the polygonal boundary?
|
intersected_boundary.clear();
|
||||||
const IfcVector3& e0_plane = proj * e0;
|
IntersectsBoundaryProfile(e0, e1, profile->verts, isCurrentlyInside, intersected_boundary);
|
||||||
const IfcVector3& e1_plane = proj * e1;
|
// sort the hits by distance from e0 to get the correct in/out/in sequence. Manually :-( I miss you, C++11.
|
||||||
|
if( intersected_boundary.size() > 1 )
|
||||||
intersected_boundary_segments.clear();
|
{
|
||||||
intersected_boundary_points.clear();
|
bool keepSorting = true;
|
||||||
|
while( keepSorting )
|
||||||
const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts);
|
{
|
||||||
const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary;
|
keepSorting = false;
|
||||||
|
for( size_t b = 0; b < intersected_boundary.size() - 1; ++b )
|
||||||
IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts,
|
{
|
||||||
intersected_boundary_segments,
|
if( (intersected_boundary[b + 1].second - e0).SquareLength() < (intersected_boundary[b].second - e0).SquareLength() )
|
||||||
intersected_boundary_points);
|
{
|
||||||
|
keepSorting = true;
|
||||||
ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty());
|
std::swap(intersected_boundary[b + 1], intersected_boundary[b]);
|
||||||
|
}
|
||||||
// does the current segment intersect the plane?
|
}
|
||||||
// (no extra check if this is an extra point)
|
|
||||||
IfcVector3 isectpos;
|
|
||||||
const Intersect isect = extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos);
|
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_DEBUG
|
|
||||||
if (isect == Intersect_Yes) {
|
|
||||||
const IfcFloat f = std::fabs((isectpos - p)*n);
|
|
||||||
ai_assert(f < 1e-5);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const bool is_white_side = (e0-p)*n >= -1e-6;
|
|
||||||
|
|
||||||
// e0 on good side of plane? (i.e. we should keep all geometry on this side)
|
|
||||||
if (is_white_side) {
|
|
||||||
// but is there an intersection in e0-e1 and is e1 in the clipping
|
|
||||||
// boundary? In this case, generate a line that only goes to the
|
|
||||||
// intersection point.
|
|
||||||
if (isect == Intersect_Yes && !is_outside_boundary) {
|
|
||||||
outvert.push_back(e0);
|
|
||||||
++newcount;
|
|
||||||
|
|
||||||
outvert.push_back(isectpos);
|
|
||||||
++newcount;
|
|
||||||
|
|
||||||
/*
|
|
||||||
// this is, however, only a line that goes to the plane, but not
|
|
||||||
// necessarily to the point where the bounding volume on the
|
|
||||||
// black side of the plane is hit. So basically, we need another
|
|
||||||
// check for [isectpos-e1], which should yield an intersection
|
|
||||||
// point.
|
|
||||||
extra_point_flag = true;
|
|
||||||
extra_point = isectpos;
|
|
||||||
|
|
||||||
was_outside_boundary = true;
|
|
||||||
continue; */
|
|
||||||
|
|
||||||
// [isectpos, enter_volume] potentially needs extra points.
|
|
||||||
// For this, we determine the intersection point with the
|
|
||||||
// bounding volume and project it onto the plane.
|
|
||||||
/*
|
|
||||||
const IfcVector3& enter_volume_proj = proj * enter_volume;
|
|
||||||
const IfcVector3& enter_isectpos = proj * isectpos;
|
|
||||||
|
|
||||||
intersected_boundary_segments.clear();
|
|
||||||
intersected_boundary_points.clear();
|
|
||||||
|
|
||||||
IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts,
|
|
||||||
intersected_boundary_segments,
|
|
||||||
intersected_boundary_points);
|
|
||||||
|
|
||||||
if(!intersected_boundary_segments.empty()) {
|
|
||||||
|
|
||||||
vec = vec + ((p - vec) * n) * n;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
//entered_volume_flag = true;
|
|
||||||
}
|
}
|
||||||
else {
|
// now add them to the list of intersections
|
||||||
outvert.push_back(e0);
|
for( size_t b = 0; b < intersected_boundary.size(); ++b )
|
||||||
++newcount;
|
intersections.push_back(boost::make_tuple(a, proj_inv * intersected_boundary[b].second, intersected_boundary[b].first));
|
||||||
|
|
||||||
|
// and calculate our new inside/outside state
|
||||||
|
if( intersected_boundary.size() & 1 )
|
||||||
|
isCurrentlyInside = !isCurrentlyInside;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got a list of in-out-combinations of intersections. That should be an even number of intersections, or
|
||||||
|
// we're fucked.
|
||||||
|
if( (intersections.size() & 1) != 0 )
|
||||||
|
{
|
||||||
|
IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( intersections.size() > 1 )
|
||||||
|
{
|
||||||
|
// If we started outside, the first intersection is a out->in intersection. Cycle them so that it
|
||||||
|
// starts with an intersection leaving the boundary
|
||||||
|
if( !startedInside )
|
||||||
|
for( size_t b = 0; b < intersections.size() - 1; ++b )
|
||||||
|
std::swap(intersections[b], intersections[(b + intersections.size() - 1) % intersections.size()]);
|
||||||
|
|
||||||
|
// Filter pairs of out->in->out that lie too close to each other.
|
||||||
|
for( size_t a = 0; intersections.size() > 0 && a < intersections.size() - 1; /**/ )
|
||||||
|
{
|
||||||
|
if( (intersections[a].get<1>() - intersections[(a + 1) % intersections.size()].get<1>()).SquareLength() < 1e-10 )
|
||||||
|
intersections.erase(intersections.begin() + a, intersections.begin() + a + 2);
|
||||||
|
else
|
||||||
|
a++;
|
||||||
|
}
|
||||||
|
if( intersections.size() > 1 && (intersections.back().get<1>() - intersections.front().get<1>()).SquareLength() < 1e-10 )
|
||||||
|
{
|
||||||
|
intersections.pop_back(); intersections.erase(intersections.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side,
|
|
||||||
// but only if it is within the bounding volume).
|
|
||||||
else if (isect == Intersect_Yes) {
|
|
||||||
// is e0 within the clipping volume? Insert the intersection point
|
|
||||||
// of [e0,e1] and the plane instead of e0.
|
|
||||||
if(was_outside_boundary) {
|
|
||||||
outvert.push_back(e0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(entered_volume_flag) {
|
|
||||||
const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n;
|
|
||||||
outvert.push_back(fix_point);
|
|
||||||
++newcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
outvert.push_back(isectpos);
|
|
||||||
|
// no intersections at all: either completely inside the boundary, so everything gets discarded, or completely outside.
|
||||||
|
// in the latter case we're implementional lost. I'm simply going to ignore this, so a large poly will not get any
|
||||||
|
// holes if the boundary is smaller and does not touch it anywhere.
|
||||||
|
if( intersections.empty() )
|
||||||
|
{
|
||||||
|
// starting point was outside -> everything is outside the boundary -> nothing is clipped -> add black side
|
||||||
|
// to result mesh unchanged
|
||||||
|
if( !startedInside )
|
||||||
|
{
|
||||||
|
outvertcnt.push_back(blackside.size());
|
||||||
|
outvert.insert(outvert.end(), blackside.begin(), blackside.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// starting point was inside the boundary -> everything is inside the boundary -> nothing is spared from the
|
||||||
|
// clipping -> nothing left to add to the result mesh
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
entered_volume_flag = false;
|
|
||||||
++newcount;
|
|
||||||
}
|
}
|
||||||
else { // no intersection with plane or parallel; e0,e1 are on the bad side
|
|
||||||
|
|
||||||
// did we just pass the boundary line to the poly bounding?
|
// determine the direction in which we're marching along the boundary polygon. If the src poly is faced upwards
|
||||||
if (is_boundary_intersection) {
|
// and the boundary is also winded this way, we need to march *backwards* on the boundary.
|
||||||
|
const IfcVector3 polyNormal = IfcMatrix3(proj) * TempMesh::ComputePolygonNormal(blackside.data(), blackside.size());
|
||||||
|
bool marchBackwardsOnBoundary = (profileNormal * polyNormal) >= 0.0;
|
||||||
|
|
||||||
// and are now outside the clipping boundary?
|
// Build closed loops from these intersections. Starting from an intersection leaving the boundary we
|
||||||
if (is_outside_boundary) {
|
// walk along the polygon to the next intersection (which should be an IS entering the boundary poly).
|
||||||
// in this case, get the point where the clipping boundary
|
// From there we walk along the boundary until we hit another intersection leaving the boundary,
|
||||||
// was entered first. Then, get the point where the clipping
|
// walk along the poly to the next IS and so on until we're back at the starting point.
|
||||||
// boundary volume was left! These two points with the plane
|
// We remove every intersection we "used up", so any remaining intersection is the start of a new loop.
|
||||||
// normal form another plane that intersects the clipping
|
while( !intersections.empty() )
|
||||||
// volume. There are two ways to get from the first to the
|
{
|
||||||
// second point along the intersection curve, try to pick the
|
std::vector<IfcVector3> resultpoly;
|
||||||
// one that lies within the current polygon.
|
size_t currentIntersecIdx = 0;
|
||||||
|
|
||||||
// TODO this approach doesn't handle all cases
|
while( true )
|
||||||
|
{
|
||||||
|
ai_assert(intersections.size() > currentIntersecIdx + 1);
|
||||||
|
boost::tuple<size_t, IfcVector3, size_t> currintsec = intersections[currentIntersecIdx + 0];
|
||||||
|
boost::tuple<size_t, IfcVector3, size_t> nextintsec = intersections[currentIntersecIdx + 1];
|
||||||
|
intersections.erase(intersections.begin() + currentIntersecIdx, intersections.begin() + currentIntersecIdx + 2);
|
||||||
|
|
||||||
// ...
|
// we start with an in->out intersection
|
||||||
|
resultpoly.push_back(currintsec.get<1>());
|
||||||
|
// climb along the polygon to the next intersection, which should be an out->in
|
||||||
|
size_t numPolyPoints = (currintsec.get<0>() > nextintsec.get<0>() ? blackside.size() : 0)
|
||||||
|
+ nextintsec.get<0>() - currintsec.get<0>();
|
||||||
|
for( size_t a = 1; a <= numPolyPoints; ++a )
|
||||||
|
resultpoly.push_back(blackside[(currintsec.get<0>() + a) % blackside.size()]);
|
||||||
|
// put the out->in intersection
|
||||||
|
resultpoly.push_back(nextintsec.get<1>());
|
||||||
|
|
||||||
IfcFloat d = 1e20;
|
// generate segments along the boundary polygon that lie in the poly's plane until we hit another intersection
|
||||||
IfcVector3 vclosest;
|
IfcVector3 startingPoint = proj * nextintsec.get<1>();
|
||||||
BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
|
size_t currentBoundaryEdgeIdx = (nextintsec.get<2>() + (marchBackwardsOnBoundary ? 1 : 0)) % profile->verts.size();
|
||||||
const IfcFloat dn = (v-e1_plane).SquareLength();
|
size_t nextIntsecIdx = SIZE_MAX;
|
||||||
if (dn < d) {
|
while( nextIntsecIdx == SIZE_MAX )
|
||||||
d = dn;
|
{
|
||||||
vclosest = v;
|
IfcFloat t = 1e10;
|
||||||
|
|
||||||
|
size_t nextBoundaryEdgeIdx = marchBackwardsOnBoundary ? (currentBoundaryEdgeIdx + profile->verts.size() - 1) : currentBoundaryEdgeIdx + 1;
|
||||||
|
nextBoundaryEdgeIdx %= profile->verts.size();
|
||||||
|
// vertices of the current boundary segments
|
||||||
|
IfcVector3 currBoundaryPoint = profile->verts[currentBoundaryEdgeIdx];
|
||||||
|
IfcVector3 nextBoundaryPoint = profile->verts[nextBoundaryEdgeIdx];
|
||||||
|
|
||||||
|
// build a direction that goes along the boundary border but lies in the poly plane
|
||||||
|
IfcVector3 boundaryPlaneNormal = ((nextBoundaryPoint - currBoundaryPoint) ^ profileNormal).Normalize();
|
||||||
|
IfcVector3 dirAtPolyPlane = (boundaryPlaneNormal ^ polyNormal).Normalize() * (marchBackwardsOnBoundary ? -1.0 : 1.0);
|
||||||
|
// if we can project the direction to the plane, we can calculate a maximum marching distance along that dir
|
||||||
|
// until we finish that boundary segment and continue on the next
|
||||||
|
if( std::abs(polyNormal.z) > 1e-5 )
|
||||||
|
{
|
||||||
|
t = std::min(t, (nextBoundaryPoint - startingPoint).Length() / std::abs(polyNormal.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the direction hits the loop start - if yes, we got a poly to output
|
||||||
|
IfcVector3 dirToThatPoint = proj * resultpoly.front() - startingPoint;
|
||||||
|
IfcFloat tpt = dirToThatPoint * dirAtPolyPlane;
|
||||||
|
if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
|
||||||
|
{
|
||||||
|
nextIntsecIdx = intersections.size(); // dirty hack to end marching along the boundary and signal the end of the loop
|
||||||
|
t = tpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// also check if the direction hits any in->out intersections earlier. If we hit one, we can switch back
|
||||||
|
// to marching along the poly border from that intersection point
|
||||||
|
for( size_t a = 0; a < intersections.size(); a += 2 )
|
||||||
|
{
|
||||||
|
dirToThatPoint = proj * intersections[a].get<1>() - startingPoint;
|
||||||
|
tpt = dirToThatPoint * dirAtPolyPlane;
|
||||||
|
if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
|
||||||
|
{
|
||||||
|
nextIntsecIdx = a; // switch back to poly and march on from this in->out intersection
|
||||||
|
t = tpt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vclosest = proj_inv * vclosest;
|
// if we keep marching on the boundary, put the segment end point to the result poly and well... keep marching
|
||||||
if(entered_volume_flag) {
|
if( nextIntsecIdx == SIZE_MAX )
|
||||||
const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n;
|
{
|
||||||
outvert.push_back(fix_point);
|
resultpoly.push_back(proj_inv * nextBoundaryPoint);
|
||||||
++newcount;
|
currentBoundaryEdgeIdx = nextBoundaryEdgeIdx;
|
||||||
|
startingPoint = nextBoundaryPoint;
|
||||||
entered_volume_flag = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outvert.push_back(vclosest);
|
// quick endless loop check
|
||||||
++newcount;
|
if( resultpoly.size() > blackside.size() + profile->verts.size() )
|
||||||
|
{
|
||||||
//outvert.push_back(e1);
|
IFCImporter::LogError("Encountered endless loop while clipping polygon against poly-bounded half space.");
|
||||||
//++newcount;
|
break;
|
||||||
}
|
|
||||||
else {
|
|
||||||
entered_volume_flag = true;
|
|
||||||
|
|
||||||
// we just entered the clipping boundary. Record the point
|
|
||||||
// and the segment where we entered and also generate this point.
|
|
||||||
//last_intersected_boundary_segment = intersected_boundary_segments.front();
|
|
||||||
//last_intersected_boundary_point = intersected_boundary_points.front();
|
|
||||||
|
|
||||||
outvert.push_back(e0);
|
|
||||||
++newcount;
|
|
||||||
|
|
||||||
IfcFloat d = 1e20;
|
|
||||||
IfcVector3 vclosest;
|
|
||||||
BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
|
|
||||||
const IfcFloat dn = (v-e0_plane).SquareLength();
|
|
||||||
if (dn < d) {
|
|
||||||
d = dn;
|
|
||||||
vclosest = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enter_volume = proj_inv * vclosest;
|
|
||||||
outvert.push_back(enter_volume);
|
|
||||||
++newcount;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// if not, we just keep the vertex
|
|
||||||
else if (is_outside_boundary) {
|
|
||||||
outvert.push_back(e0);
|
|
||||||
++newcount;
|
|
||||||
|
|
||||||
entered_volume_flag = false;
|
// we're back on the poly - if this is the intersection we started from, we got a closed loop.
|
||||||
|
if( nextIntsecIdx >= intersections.size() )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise it's another intersection. Continue marching from there.
|
||||||
|
currentIntersecIdx = nextIntsecIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WritePolygon(resultpoly, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
was_outside_boundary = is_outside_boundary;
|
|
||||||
extra_point_flag = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newcount) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
IfcVector3 vmin,vmax;
|
|
||||||
ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
|
|
||||||
|
|
||||||
// filter our IfcFloat points - those may happen if a point lies
|
|
||||||
// directly on the intersection line. However, due to IfcFloat
|
|
||||||
// precision a bitwise comparison is not feasible to detect
|
|
||||||
// this case.
|
|
||||||
const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
|
|
||||||
FuzzyVectorCompare fz(epsilon);
|
|
||||||
|
|
||||||
std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
|
|
||||||
|
|
||||||
if (e != outvert.end()) {
|
|
||||||
newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
|
|
||||||
outvert.erase(e,outvert.end());
|
|
||||||
}
|
|
||||||
if (fz(*( outvert.end()-newcount),outvert.back())) {
|
|
||||||
outvert.pop_back();
|
|
||||||
--newcount;
|
|
||||||
}
|
|
||||||
if(newcount > 2) {
|
|
||||||
result.vertcnt.push_back(newcount);
|
|
||||||
}
|
|
||||||
else while(newcount-->0) {
|
|
||||||
result.verts.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");
|
IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");
|
||||||
}
|
}
|
||||||
|
|
|
@ -579,6 +579,11 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
|
||||||
IfcVector3 min = in[0];
|
IfcVector3 min = in[0];
|
||||||
dir *= IfcMatrix3(trafo);
|
dir *= IfcMatrix3(trafo);
|
||||||
|
|
||||||
|
// reverse profile polygon if it's winded in the wrong direction in relation to the extrusion direction
|
||||||
|
IfcVector3 profileNormal = TempMesh::ComputePolygonNormal( in.data(), in.size());
|
||||||
|
if( profileNormal * dir < 0.0 )
|
||||||
|
std::reverse( in.begin(), in.end());
|
||||||
|
|
||||||
std::vector<IfcVector3> nors;
|
std::vector<IfcVector3> nors;
|
||||||
const bool openings = !!conv.apply_openings && conv.apply_openings->size();
|
const bool openings = !!conv.apply_openings && conv.apply_openings->size();
|
||||||
|
|
||||||
|
@ -619,9 +624,9 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
|
||||||
curmesh.vertcnt.push_back(4);
|
curmesh.vertcnt.push_back(4);
|
||||||
|
|
||||||
out.push_back(in[i]);
|
out.push_back(in[i]);
|
||||||
out.push_back(in[i]+dir);
|
|
||||||
out.push_back(in[next]+dir);
|
|
||||||
out.push_back(in[next]);
|
out.push_back(in[next]);
|
||||||
|
out.push_back(in[next]+dir);
|
||||||
|
out.push_back(in[i]+dir);
|
||||||
|
|
||||||
if(openings) {
|
if(openings) {
|
||||||
if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) {
|
if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) {
|
||||||
|
@ -646,8 +651,12 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
|
||||||
if(has_area) {
|
if(has_area) {
|
||||||
|
|
||||||
for(size_t n = 0; n < 2; ++n) {
|
for(size_t n = 0; n < 2; ++n) {
|
||||||
for(size_t i = size; i--; ) {
|
if( n > 0 ) {
|
||||||
out.push_back(in[i]+(n?dir:IfcVector3()));
|
for(size_t i = 0; i < size; ++i )
|
||||||
|
out.push_back(in[i]+dir);
|
||||||
|
} else {
|
||||||
|
for(size_t i = size; i--; )
|
||||||
|
out.push_back(in[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
curmesh.vertcnt.push_back(size);
|
curmesh.vertcnt.push_back(size);
|
||||||
|
@ -699,10 +708,10 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned int>& mesh_indices,
|
bool ProcessGeometricItem(const IfcRepresentationItem& geo, unsigned int matid, std::vector<unsigned int>& mesh_indices,
|
||||||
ConversionData& conv)
|
ConversionData& conv)
|
||||||
{
|
{
|
||||||
bool fix_orientation = true;
|
bool fix_orientation = false;
|
||||||
boost::shared_ptr< TempMesh > meshtmp = boost::make_shared<TempMesh>();
|
boost::shared_ptr< TempMesh > meshtmp = boost::make_shared<TempMesh>();
|
||||||
if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<IfcShellBasedSurfaceModel>()) {
|
if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<IfcShellBasedSurfaceModel>()) {
|
||||||
BOOST_FOREACH(boost::shared_ptr<const IfcShell> shell,shellmod->SbsmBoundary) {
|
BOOST_FOREACH(boost::shared_ptr<const IfcShell> shell,shellmod->SbsmBoundary) {
|
||||||
|
@ -716,24 +725,27 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned
|
||||||
IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
|
IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fix_orientation = true;
|
||||||
}
|
}
|
||||||
else if(const IfcConnectedFaceSet* fset = geo.ToPtr<IfcConnectedFaceSet>()) {
|
else if(const IfcConnectedFaceSet* fset = geo.ToPtr<IfcConnectedFaceSet>()) {
|
||||||
ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv);
|
ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv);
|
||||||
|
fix_orientation = true;
|
||||||
}
|
}
|
||||||
else if(const IfcSweptAreaSolid* swept = geo.ToPtr<IfcSweptAreaSolid>()) {
|
else if(const IfcSweptAreaSolid* swept = geo.ToPtr<IfcSweptAreaSolid>()) {
|
||||||
ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv);
|
ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv);
|
||||||
}
|
}
|
||||||
else if(const IfcSweptDiskSolid* disk = geo.ToPtr<IfcSweptDiskSolid>()) {
|
else if(const IfcSweptDiskSolid* disk = geo.ToPtr<IfcSweptDiskSolid>()) {
|
||||||
ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv);
|
ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv);
|
||||||
fix_orientation = false;
|
|
||||||
}
|
}
|
||||||
else if(const IfcManifoldSolidBrep* brep = geo.ToPtr<IfcManifoldSolidBrep>()) {
|
else if(const IfcManifoldSolidBrep* brep = geo.ToPtr<IfcManifoldSolidBrep>()) {
|
||||||
ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv);
|
ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv);
|
||||||
|
fix_orientation = true;
|
||||||
}
|
}
|
||||||
else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr<IfcFaceBasedSurfaceModel>()) {
|
else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr<IfcFaceBasedSurfaceModel>()) {
|
||||||
BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) {
|
BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) {
|
||||||
ProcessConnectedFaceSet(fc,*meshtmp.get(),conv);
|
ProcessConnectedFaceSet(fc,*meshtmp.get(),conv);
|
||||||
}
|
}
|
||||||
|
fix_orientation = true;
|
||||||
}
|
}
|
||||||
else if(const IfcBooleanResult* boolean = geo.ToPtr<IfcBooleanResult>()) {
|
else if(const IfcBooleanResult* boolean = geo.ToPtr<IfcBooleanResult>()) {
|
||||||
ProcessBoolean(*boolean,*meshtmp.get(),conv);
|
ProcessBoolean(*boolean,*meshtmp.get(),conv);
|
||||||
|
@ -777,7 +789,7 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned
|
||||||
|
|
||||||
aiMesh* const mesh = meshtmp->ToMesh();
|
aiMesh* const mesh = meshtmp->ToMesh();
|
||||||
if(mesh) {
|
if(mesh) {
|
||||||
mesh->mMaterialIndex = ProcessMaterials(geo,conv);
|
mesh->mMaterialIndex = matid;
|
||||||
mesh_indices.push_back(conv.meshes.size());
|
mesh_indices.push_back(conv.meshes.size());
|
||||||
conv.meshes.push_back(mesh);
|
conv.meshes.push_back(mesh);
|
||||||
return true;
|
return true;
|
||||||
|
@ -807,10 +819,11 @@ void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool TryQueryMeshCache(const IfcRepresentationItem& item,
|
bool TryQueryMeshCache(const IfcRepresentationItem& item,
|
||||||
std::vector<unsigned int>& mesh_indices,
|
std::vector<unsigned int>& mesh_indices, unsigned int mat_index,
|
||||||
ConversionData& conv)
|
ConversionData& conv)
|
||||||
{
|
{
|
||||||
ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(&item);
|
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
||||||
|
ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(idx);
|
||||||
if (it != conv.cached_meshes.end()) {
|
if (it != conv.cached_meshes.end()) {
|
||||||
std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices));
|
std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices));
|
||||||
return true;
|
return true;
|
||||||
|
@ -820,21 +833,25 @@ bool TryQueryMeshCache(const IfcRepresentationItem& item,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void PopulateMeshCache(const IfcRepresentationItem& item,
|
void PopulateMeshCache(const IfcRepresentationItem& item,
|
||||||
const std::vector<unsigned int>& mesh_indices,
|
const std::vector<unsigned int>& mesh_indices, unsigned int mat_index,
|
||||||
ConversionData& conv)
|
ConversionData& conv)
|
||||||
{
|
{
|
||||||
conv.cached_meshes[&item] = mesh_indices;
|
ConversionData::MeshCacheIndex idx(&item, mat_index);
|
||||||
|
conv.cached_meshes[idx] = mesh_indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessRepresentationItem(const IfcRepresentationItem& item,
|
bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid,
|
||||||
std::vector<unsigned int>& mesh_indices,
|
std::vector<unsigned int>& mesh_indices,
|
||||||
ConversionData& conv)
|
ConversionData& conv)
|
||||||
{
|
{
|
||||||
if (!TryQueryMeshCache(item,mesh_indices,conv)) {
|
// determine material
|
||||||
if(ProcessGeometricItem(item,mesh_indices,conv)) {
|
unsigned int localmatid = ProcessMaterials(item.GetID(), matid, conv, true);
|
||||||
|
|
||||||
|
if (!TryQueryMeshCache(item,mesh_indices,localmatid,conv)) {
|
||||||
|
if(ProcessGeometricItem(item,localmatid,mesh_indices,conv)) {
|
||||||
if(mesh_indices.size()) {
|
if(mesh_indices.size()) {
|
||||||
PopulateMeshCache(item,mesh_indices,conv);
|
PopulateMeshCache(item,mesh_indices,localmatid,conv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else return false;
|
else return false;
|
||||||
|
|
|
@ -428,7 +428,7 @@ void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
|
bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, unsigned int matid, ConversionData& conv)
|
||||||
{
|
{
|
||||||
// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
|
// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
|
||||||
std::auto_ptr<aiNode> nd(new aiNode());
|
std::auto_ptr<aiNode> nd(new aiNode());
|
||||||
|
@ -453,11 +453,12 @@ bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int localmatid = ProcessMaterials(mapped.GetID(),matid,conv,false);
|
||||||
const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
|
const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
|
||||||
|
|
||||||
bool got = false;
|
bool got = false;
|
||||||
BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
|
BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
|
||||||
if(!ProcessRepresentationItem(item,meshes,conv)) {
|
if(!ProcessRepresentationItem(item,localmatid,meshes,conv)) {
|
||||||
IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
|
IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
|
||||||
}
|
}
|
||||||
else got = true;
|
else got = true;
|
||||||
|
@ -557,7 +558,11 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
|
||||||
if(!el.Representation) {
|
if(!el.Representation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract Color from metadata, if present
|
||||||
|
unsigned int matid = ProcessMaterials( el.GetID(), UINT32_MAX, conv, false);
|
||||||
std::vector<unsigned int> meshes;
|
std::vector<unsigned int> meshes;
|
||||||
|
|
||||||
// we want only one representation type, so bring them in a suitable order (i.e try those
|
// we want only one representation type, so bring them in a suitable order (i.e try those
|
||||||
// that look as if we could read them quickly at first). This way of reading
|
// that look as if we could read them quickly at first). This way of reading
|
||||||
// representation is relatively generic and allows the concrete implementations
|
// representation is relatively generic and allows the concrete implementations
|
||||||
|
@ -571,10 +576,10 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
|
||||||
bool res = false;
|
bool res = false;
|
||||||
BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
|
BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
|
||||||
if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
|
if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
|
||||||
res = ProcessMappedItem(*geo,nd,subnodes,conv) || res;
|
res = ProcessMappedItem(*geo,nd,subnodes,matid,conv) || res;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = ProcessRepresentationItem(item,meshes,conv) || res;
|
res = ProcessRepresentationItem(item,matid,meshes,conv) || res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we got something meaningful at this point, skip any further representations
|
// if we got something meaningful at this point, skip any further representations
|
||||||
|
|
|
@ -132,45 +132,70 @@ void FillMaterial(aiMaterial* mat,const IFC::IfcSurfaceStyle* surf,ConversionDat
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv)
|
unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat)
|
||||||
{
|
{
|
||||||
if (conv.materials.empty()) {
|
STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(id);
|
||||||
aiString name;
|
|
||||||
std::auto_ptr<aiMaterial> mat(new aiMaterial());
|
|
||||||
|
|
||||||
name.Set("<IFCDefault>");
|
|
||||||
mat->AddProperty(&name,AI_MATKEY_NAME);
|
|
||||||
|
|
||||||
const aiColor4D col = aiColor4D(0.6f,0.6f,0.6f,1.0f);
|
|
||||||
mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE);
|
|
||||||
|
|
||||||
conv.materials.push_back(mat.release());
|
|
||||||
}
|
|
||||||
|
|
||||||
STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(item.GetID());
|
|
||||||
for(;range.first != range.second; ++range.first) {
|
for(;range.first != range.second; ++range.first) {
|
||||||
if(const IFC::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr<IFC::IfcStyledItem>()) {
|
if(const IFC::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr<IFC::IfcStyledItem>()) {
|
||||||
BOOST_FOREACH(const IFC::IfcPresentationStyleAssignment& as, styled->Styles) {
|
BOOST_FOREACH(const IFC::IfcPresentationStyleAssignment& as, styled->Styles) {
|
||||||
BOOST_FOREACH(boost::shared_ptr<const IFC::IfcPresentationStyleSelect> sel, as.Styles) {
|
BOOST_FOREACH(boost::shared_ptr<const IFC::IfcPresentationStyleSelect> sel, as.Styles) {
|
||||||
|
|
||||||
if (const IFC::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr<IFC::IfcSurfaceStyle>(conv.db)) {
|
if( const IFC::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr<IFC::IfcSurfaceStyle>(conv.db) ) {
|
||||||
|
// try to satisfy from cache
|
||||||
|
ConversionData::MaterialCache::iterator mit = conv.cached_materials.find(surf);
|
||||||
|
if( mit != conv.cached_materials.end() )
|
||||||
|
return mit->second;
|
||||||
|
|
||||||
|
// not found, create new material
|
||||||
const std::string side = static_cast<std::string>(surf->Side);
|
const std::string side = static_cast<std::string>(surf->Side);
|
||||||
if (side != "BOTH") {
|
if( side != "BOTH" ) {
|
||||||
IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: " + side);
|
IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: " + side);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::auto_ptr<aiMaterial> mat(new aiMaterial());
|
std::auto_ptr<aiMaterial> mat(new aiMaterial());
|
||||||
|
|
||||||
FillMaterial(mat.get(),surf,conv);
|
FillMaterial(mat.get(), surf, conv);
|
||||||
|
|
||||||
conv.materials.push_back(mat.release());
|
conv.materials.push_back(mat.release());
|
||||||
return conv.materials.size()-1;
|
unsigned int matindex = conv.materials.size() - 1;
|
||||||
}
|
conv.cached_materials[surf] = matindex;
|
||||||
}
|
return matindex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no local material defined. If there's global one, use that instead
|
||||||
|
if( prevMatId != UINT32_MAX )
|
||||||
|
return prevMatId;
|
||||||
|
|
||||||
|
// we're still here - create an default material if required, or simply fail otherwise
|
||||||
|
if( !forceDefaultMat )
|
||||||
|
return UINT32_MAX;
|
||||||
|
|
||||||
|
aiString name;
|
||||||
|
name.Set("<IFCDefault>");
|
||||||
|
// ConvertColorToString( color, name);
|
||||||
|
|
||||||
|
// look if there's already a default material with this base color
|
||||||
|
for( size_t a = 0; a < conv.materials.size(); ++a )
|
||||||
|
{
|
||||||
|
aiString mname;
|
||||||
|
conv.materials[a]->Get(AI_MATKEY_NAME, mname);
|
||||||
|
if( name == mname )
|
||||||
|
return (unsigned int)a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're here, yet - no default material with suitable color available. Generate one
|
||||||
|
std::auto_ptr<aiMaterial> mat(new aiMaterial());
|
||||||
|
mat->AddProperty(&name,AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
const aiColor4D col = aiColor4D( 0.6f, 0.6f, 0.6f, 1.0f); // aiColor4D( color.r, color.g, color.b, 1.0f);
|
||||||
|
mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
|
||||||
|
conv.materials.push_back(mat.release());
|
||||||
|
return (unsigned int) conv.materials.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ! IFC
|
} // ! IFC
|
||||||
|
|
|
@ -902,6 +902,14 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
curmesh.verts.reserve(curmesh.verts.size() + (*it).contour.size() * 4);
|
curmesh.verts.reserve(curmesh.verts.size() + (*it).contour.size() * 4);
|
||||||
curmesh.vertcnt.reserve(curmesh.vertcnt.size() + (*it).contour.size());
|
curmesh.vertcnt.reserve(curmesh.vertcnt.size() + (*it).contour.size());
|
||||||
|
|
||||||
|
// compare base poly normal and contour normal to detect if we need to reverse the face winding
|
||||||
|
IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal( curmesh.verts.data(), curmesh.vertcnt.front());
|
||||||
|
std::vector<IfcVector3> worldSpaceContourVtx( it->contour.size());
|
||||||
|
for( size_t a = 0; a < it->contour.size(); ++a )
|
||||||
|
worldSpaceContourVtx[a] = minv * IfcVector3( it->contour[a].x, it->contour[a].y, 0.0);
|
||||||
|
IfcVector3 contourNormal = TempMesh::ComputePolygonNormal( worldSpaceContourVtx.data(), worldSpaceContourVtx.size());
|
||||||
|
bool reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0;
|
||||||
|
|
||||||
// XXX this algorithm is really a bit inefficient - both in terms
|
// XXX this algorithm is really a bit inefficient - both in terms
|
||||||
// of constant factor and of asymptotic runtime.
|
// of constant factor and of asymptotic runtime.
|
||||||
std::vector<bool>::const_iterator skipit = skipbegin;
|
std::vector<bool>::const_iterator skipit = skipbegin;
|
||||||
|
@ -909,9 +917,6 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
IfcVector3 start0;
|
IfcVector3 start0;
|
||||||
IfcVector3 start1;
|
IfcVector3 start1;
|
||||||
|
|
||||||
IfcVector2 last_proj;
|
|
||||||
//const IfcVector2& first_proj;
|
|
||||||
|
|
||||||
const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end();
|
const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end();
|
||||||
|
|
||||||
bool drop_this_edge = false;
|
bool drop_this_edge = false;
|
||||||
|
@ -923,18 +928,8 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
IfcFloat best = static_cast<IfcFloat>(1e10);
|
IfcFloat best = static_cast<IfcFloat>(1e10);
|
||||||
IfcVector3 bestv;
|
IfcVector3 bestv;
|
||||||
|
|
||||||
/* debug code to check for unwanted diagonal lines in window contours
|
|
||||||
if (cit != cbegin) {
|
|
||||||
const IfcVector2& vdelta = proj_point - last_proj;
|
|
||||||
if (std::fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) {
|
|
||||||
//continue;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f);
|
const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f);
|
||||||
|
|
||||||
last_proj = proj_point;
|
|
||||||
|
|
||||||
BOOST_FOREACH(const TempOpening* opening, refs) {
|
BOOST_FOREACH(const TempOpening* opening, refs) {
|
||||||
BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) {
|
BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) {
|
||||||
const IfcFloat sqdist = (world_point - other).SquareLength();
|
const IfcFloat sqdist = (world_point - other).SquareLength();
|
||||||
|
@ -956,8 +951,8 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
curmesh.verts.pop_back();
|
curmesh.verts.pop_back();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
curmesh.verts.push_back(cit == cbegin ? world_point : bestv);
|
curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? world_point : bestv);
|
||||||
curmesh.verts.push_back(cit == cbegin ? bestv : world_point);
|
curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? bestv : world_point);
|
||||||
|
|
||||||
curmesh.vertcnt.push_back(4);
|
curmesh.vertcnt.push_back(4);
|
||||||
++closed;
|
++closed;
|
||||||
|
@ -969,8 +964,8 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
curmesh.verts.push_back(world_point);
|
curmesh.verts.push_back(reverseCountourFaces ? bestv : world_point);
|
||||||
curmesh.verts.push_back(bestv);
|
curmesh.verts.push_back(reverseCountourFaces ? world_point : bestv);
|
||||||
|
|
||||||
if (cit == cend - 1) {
|
if (cit == cend - 1) {
|
||||||
drop_this_edge = *skipit;
|
drop_this_edge = *skipit;
|
||||||
|
@ -984,16 +979,11 @@ size_t CloseWindows(ContourVector& contours,
|
||||||
curmesh.verts.pop_back();
|
curmesh.verts.pop_back();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
curmesh.verts.push_back(start1);
|
curmesh.verts.push_back(reverseCountourFaces ? start0 : start1);
|
||||||
curmesh.verts.push_back(start0);
|
curmesh.verts.push_back(reverseCountourFaces ? start1 : start0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
BOOST_FOREACH(TempOpening* opening, refs) {
|
|
||||||
//opening->wallPoints.clear();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
|
170
code/IFCUtil.cpp
170
code/IFCUtil.cpp
|
@ -166,6 +166,23 @@ void TempMesh::RemoveDegenerates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
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];
|
||||||
|
temp[i++] = v.x;
|
||||||
|
temp[i++] = v.y;
|
||||||
|
temp[i++] = v.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
IfcVector3 nor;
|
||||||
|
NewellNormal<3, 3, 3>(nor, cnt, &temp[0], &temp[1], &temp[2]);
|
||||||
|
return normalize ? nor.Normalize() : nor;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
||||||
bool normalize,
|
bool normalize,
|
||||||
|
@ -214,37 +231,148 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
||||||
// Compute the normal of the last polygon in the given mesh
|
// Compute the normal of the last polygon in the given mesh
|
||||||
IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const
|
IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const
|
||||||
{
|
{
|
||||||
size_t total = vertcnt.back(), vidx = verts.size() - total;
|
return ComputePolygonNormal(&verts[verts.size() - vertcnt.back()], vertcnt.back(), normalize);
|
||||||
std::vector<IfcFloat> temp((total+2)*3);
|
|
||||||
for(size_t vofs = 0, cnt = 0; vofs < total; ++vofs) {
|
|
||||||
const IfcVector3& v = verts[vidx+vofs];
|
|
||||||
temp[cnt++] = v.x;
|
|
||||||
temp[cnt++] = v.y;
|
|
||||||
temp[cnt++] = v.z;
|
|
||||||
}
|
|
||||||
IfcVector3 nor;
|
|
||||||
NewellNormal<3,3,3>(nor,total,&temp[0],&temp[1],&temp[2]);
|
|
||||||
return normalize ? nor.Normalize() : nor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CompareVector
|
||||||
|
{
|
||||||
|
bool operator () (const IfcVector3& a, const IfcVector3& b)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
IfcVector3 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();
|
const IfcVector3 vavg = Center();
|
||||||
|
|
||||||
std::vector<IfcVector3> normals;
|
// create a list of start indices for all faces to allow random access to faces
|
||||||
ComputePolygonNormals(normals);
|
std::vector<size_t> faceStartIndices(vertcnt.size());
|
||||||
|
for( size_t i = 0, a = 0; a < vertcnt.size(); i += vertcnt[a], ++a )
|
||||||
|
faceStartIndices[a] = i;
|
||||||
|
|
||||||
size_t c = 0, ofs = 0;
|
// list all faces on a vertex
|
||||||
BOOST_FOREACH(unsigned int cnt, vertcnt) {
|
std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
|
||||||
if (cnt>2){
|
for( size_t a = 0; a < vertcnt.size(); ++a )
|
||||||
const IfcVector3& thisvert = verts[c];
|
{
|
||||||
if (normals[ofs]*(thisvert-vavg) < 0) {
|
for( size_t b = 0; b < vertcnt[a]; ++b )
|
||||||
std::reverse(verts.begin()+c,verts.begin()+cnt+c);
|
facesByVertex[verts[faceStartIndices[a] + b]].push_back(a);
|
||||||
|
}
|
||||||
|
// determine neighbourhood for all polys
|
||||||
|
std::vector<size_t> neighbour(verts.size(), SIZE_MAX);
|
||||||
|
std::vector<size_t> tempIntersect(10);
|
||||||
|
for( size_t a = 0; a < vertcnt.size(); ++a )
|
||||||
|
{
|
||||||
|
for( size_t b = 0; b < vertcnt[a]; ++b )
|
||||||
|
{
|
||||||
|
size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % vertcnt[a];
|
||||||
|
const std::vector<size_t>& facesOnB = facesByVertex[verts[ib]];
|
||||||
|
const std::vector<size_t>& facesOnNB = facesByVertex[verts[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);
|
||||||
|
|
||||||
|
if( std::distance(sectstart, sectend) != 2 )
|
||||||
|
continue;
|
||||||
|
if( *sectstart == a )
|
||||||
|
++sectstart;
|
||||||
|
neighbour[ib] = *sectstart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we're getting started. We take the face which is the farthest away from the center. This face is most probably
|
||||||
|
// 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(vertcnt.size(), false);
|
||||||
|
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 < vertcnt.size(); ++a )
|
||||||
|
{
|
||||||
|
if( faceDone[a] )
|
||||||
|
continue;
|
||||||
|
IfcVector3 faceCenter = std::accumulate(verts.begin() + faceStartIndices[a],
|
||||||
|
verts.begin() + faceStartIndices[a] + vertcnt[a], IfcVector3(0.0)) / IfcFloat(vertcnt[a]);
|
||||||
|
IfcFloat dst = (faceCenter - vavg).SquareLength();
|
||||||
|
if( dst > farthestDistance ) { farthestDistance = dst; farthestIndex = a; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate its normal and reverse the poly if its facing towards the mesh center
|
||||||
|
IfcVector3 farthestNormal = ComputePolygonNormal(verts.data() + faceStartIndices[farthestIndex], vertcnt[farthestIndex]);
|
||||||
|
IfcVector3 farthestCenter = std::accumulate(verts.begin() + faceStartIndices[farthestIndex],
|
||||||
|
verts.begin() + faceStartIndices[farthestIndex] + vertcnt[farthestIndex], IfcVector3(0.0))
|
||||||
|
/ IfcFloat(vertcnt[farthestIndex]);
|
||||||
|
// We accapt 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 )
|
||||||
|
{
|
||||||
|
size_t fsi = faceStartIndices[farthestIndex], fvc = vertcnt[farthestIndex];
|
||||||
|
std::reverse(verts.begin() + fsi, verts.begin() + fsi + fvc);
|
||||||
|
std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
|
||||||
|
// because of the neighbour index belonging to the edge starting with the point at the same index, we need to
|
||||||
|
// cycle the neighbours through to match the edges again.
|
||||||
|
// 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 )
|
||||||
|
std::swap(neighbour[fsi + a], neighbour[fsi + a + 1]);
|
||||||
|
}
|
||||||
|
faceDone[farthestIndex] = true;
|
||||||
|
std::vector<size_t> todo;
|
||||||
|
todo.push_back(farthestIndex);
|
||||||
|
|
||||||
|
// go over its neighbour faces recursively and adapt their winding order to match the farthest face
|
||||||
|
while( !todo.empty() )
|
||||||
|
{
|
||||||
|
size_t tdf = todo.back();
|
||||||
|
size_t vsi = faceStartIndices[tdf], vc = vertcnt[tdf];
|
||||||
|
todo.pop_back();
|
||||||
|
|
||||||
|
// check its neighbours
|
||||||
|
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] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const IfcVector3& vp = verts[vsi + a];
|
||||||
|
size_t nbvsi = faceStartIndices[nbi], nbvc = vertcnt[nbi];
|
||||||
|
std::vector<IfcVector3>::iterator it = std::find_if(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc, FindVector(vp));
|
||||||
|
ai_assert(it != verts.begin() + nbvsi + nbvc);
|
||||||
|
size_t nb_vidx = std::distance(verts.begin() + nbvsi, it);
|
||||||
|
// two faces winded in the same direction should have a crossed edge, where one face has p0->p1 and the other
|
||||||
|
// has p1'->p0'. If the next point on the neighbouring face is also the next on the current face, we need
|
||||||
|
// to reverse the neighbour
|
||||||
|
nb_vidx = (nb_vidx + 1) % nbvc;
|
||||||
|
size_t oursideidx = (a + 1) % vc;
|
||||||
|
if( FuzzyVectorCompare(1e-6)(verts[vsi + oursideidx], verts[nbvsi + nb_vidx]) )
|
||||||
|
{
|
||||||
|
std::reverse(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc);
|
||||||
|
std::reverse(neighbour.begin() + nbvsi, neighbour.begin() + nbvsi + nbvc);
|
||||||
|
for( size_t a = 0; a < nbvc - 1; ++a )
|
||||||
|
std::swap(neighbour[nbvsi + a], neighbour[nbvsi + a + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// either way we're done with the neighbour. Mark it as done and continue checking from there recursively
|
||||||
|
faceDone[nbi] = true;
|
||||||
|
todo.push_back(nbi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c += cnt;
|
|
||||||
++ofs;
|
// no more faces reachable from this part of the surface, start over with a disjunct part and its farthest face
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,10 +97,10 @@ struct TempMesh
|
||||||
void RemoveDegenerates();
|
void RemoveDegenerates();
|
||||||
|
|
||||||
void FixupFaceOrientation();
|
void FixupFaceOrientation();
|
||||||
|
|
||||||
|
static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
|
||||||
IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
|
IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
|
||||||
void ComputePolygonNormals(std::vector<IfcVector3>& normals,
|
void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
|
||||||
bool normalize = true,
|
|
||||||
size_t ofs = 0) const;
|
|
||||||
|
|
||||||
void Swap(TempMesh& other);
|
void Swap(TempMesh& other);
|
||||||
};
|
};
|
||||||
|
@ -195,9 +195,19 @@ struct ConversionData
|
||||||
std::vector<aiMesh*> meshes;
|
std::vector<aiMesh*> meshes;
|
||||||
std::vector<aiMaterial*> materials;
|
std::vector<aiMaterial*> materials;
|
||||||
|
|
||||||
typedef std::map<const IFC::IfcRepresentationItem*, std::vector<unsigned int> > MeshCache;
|
struct MeshCacheIndex {
|
||||||
|
const IFC::IfcRepresentationItem* item; unsigned int matindex;
|
||||||
|
MeshCacheIndex() : item(NULL), matindex(0) { }
|
||||||
|
MeshCacheIndex(const IFC::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::vector<unsigned int> > MeshCache;
|
||||||
MeshCache cached_meshes;
|
MeshCache cached_meshes;
|
||||||
|
|
||||||
|
typedef std::map<const IFC::IfcSurfaceStyle*, unsigned int> MaterialCache;
|
||||||
|
MaterialCache cached_materials;
|
||||||
|
|
||||||
const IFCImporter::Settings& settings;
|
const IFCImporter::Settings& settings;
|
||||||
|
|
||||||
// Intermediate arrays used to resolve openings in walls: only one of them
|
// Intermediate arrays used to resolve openings in walls: only one of them
|
||||||
|
@ -220,7 +230,7 @@ struct FuzzyVectorCompare {
|
||||||
|
|
||||||
FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
|
FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
|
||||||
bool operator()(const IfcVector3& a, const IfcVector3& b) {
|
bool operator()(const IfcVector3& a, const IfcVector3& b) {
|
||||||
return std::fabs((a-b).SquareLength()) < epsilon;
|
return std::abs((a-b).SquareLength()) < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IfcFloat epsilon;
|
const IfcFloat epsilon;
|
||||||
|
@ -263,11 +273,11 @@ IfcFloat ConvertSIPrefix(const std::string& prefix);
|
||||||
bool ProcessProfile(const IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
|
bool ProcessProfile(const IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
|
||||||
|
|
||||||
// IFCMaterial.cpp
|
// IFCMaterial.cpp
|
||||||
unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv);
|
unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
|
||||||
|
|
||||||
// IFCGeometry.cpp
|
// IFCGeometry.cpp
|
||||||
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
|
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
|
||||||
bool ProcessRepresentationItem(const IfcRepresentationItem& item, std::vector<unsigned int>& mesh_indices, ConversionData& conv);
|
bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid, std::vector<unsigned int>& mesh_indices, ConversionData& conv);
|
||||||
void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
|
void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
|
||||||
|
|
||||||
void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
|
void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
|
||||||
|
|
|
@ -170,6 +170,10 @@ corresponding preprocessor flag to selectively disable formats.
|
||||||
# include "AssbinLoader.h"
|
# include "AssbinLoader.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
|
||||||
|
# include "C4DImporter.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -297,6 +301,10 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
|
||||||
#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
|
#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
|
||||||
out.push_back( new AssbinImporter() );
|
out.push_back( new AssbinImporter() );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
|
||||||
|
out.push_back( new C4DImporter() );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue