Merge branch 'master' into issue_2195_draco
commit
c917e6513f
|
@ -18,6 +18,9 @@ build
|
||||||
*.VC.db-wal
|
*.VC.db-wal
|
||||||
*.VC.opendb
|
*.VC.opendb
|
||||||
*.ipch
|
*.ipch
|
||||||
|
.vs/
|
||||||
|
out/
|
||||||
|
CMakeSettings.json
|
||||||
|
|
||||||
# Output
|
# Output
|
||||||
bin/
|
bin/
|
||||||
|
|
22
Build.md
22
Build.md
|
@ -1,6 +1,6 @@
|
||||||
# Build Instructions
|
# Build / Install Instructions
|
||||||
|
|
||||||
## Build on all platforms using vcpkg
|
## Install on all platforms using vcpkg
|
||||||
You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
|
You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Microsoft/vcpkg.git
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
@ -11,6 +11,18 @@ You can download and install assimp using the [vcpkg](https://github.com/Microso
|
||||||
```
|
```
|
||||||
The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||||
|
|
||||||
|
## Install on Ubuntu
|
||||||
|
You can install the Asset-Importer-Lib via apt:
|
||||||
|
```
|
||||||
|
sudo apt-get install assimp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install pyassimp
|
||||||
|
You need to have pip installed:
|
||||||
|
```
|
||||||
|
pip install pyassimp
|
||||||
|
```
|
||||||
|
|
||||||
## Manual build instructions
|
## Manual build instructions
|
||||||
|
|
||||||
### Install CMake
|
### Install CMake
|
||||||
|
@ -24,6 +36,12 @@ Make sure you have a working git-installation. Open a command prompt and clone t
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/assimp/assimp.git
|
git clone https://github.com/assimp/assimp.git
|
||||||
```
|
```
|
||||||
|
### Build from source:
|
||||||
|
```bash
|
||||||
|
cd assimp
|
||||||
|
cmake CMakeLists.txt
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
### Build instructions for Windows with Visual-Studio
|
### Build instructions for Windows with Visual-Studio
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ OPTION( BUILD_SHARED_LIBS
|
||||||
"Build package with shared libraries."
|
"Build package with shared libraries."
|
||||||
ON
|
ON
|
||||||
)
|
)
|
||||||
|
|
||||||
OPTION( ASSIMP_BUILD_FRAMEWORK
|
OPTION( ASSIMP_BUILD_FRAMEWORK
|
||||||
"Build package as Mac OS X Framework bundle."
|
"Build package as Mac OS X Framework bundle."
|
||||||
OFF
|
OFF
|
||||||
|
@ -133,9 +132,22 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
|
||||||
)
|
)
|
||||||
|
|
||||||
IF ( WIN32 )
|
IF ( WIN32 )
|
||||||
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
|
# Use subset of Windows.h
|
||||||
"If the Assimp view tool is built. (requires DirectX)"
|
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
|
||||||
OFF )
|
|
||||||
|
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
|
||||||
|
"If the Assimp view tool is built. (requires DirectX)"
|
||||||
|
OFF )
|
||||||
|
|
||||||
|
IF(MSVC)
|
||||||
|
OPTION( ASSIMP_INSTALL_PDB
|
||||||
|
"Install MSVC debug files."
|
||||||
|
ON )
|
||||||
|
IF(NOT (MSVC_VERSION LESS 1900))
|
||||||
|
# Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
|
||||||
|
ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
||||||
|
@ -145,21 +157,6 @@ IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
||||||
ADD_DEFINITIONS(-DENABLE_BITCODE)
|
ADD_DEFINITIONS(-DENABLE_BITCODE)
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
# Use subset of Windows.h
|
|
||||||
if (WIN32)
|
|
||||||
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
IF(MSVC)
|
|
||||||
OPTION( ASSIMP_INSTALL_PDB
|
|
||||||
"Install MSVC debug files."
|
|
||||||
ON
|
|
||||||
)
|
|
||||||
IF(NOT (MSVC_VERSION LESS 1900))
|
|
||||||
# Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
|
|
||||||
ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
|
|
||||||
ENDIF()
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF (ASSIMP_BUILD_FRAMEWORK)
|
IF (ASSIMP_BUILD_FRAMEWORK)
|
||||||
SET (BUILD_SHARED_LIBS ON)
|
SET (BUILD_SHARED_LIBS ON)
|
||||||
|
|
|
@ -266,6 +266,7 @@ void Discreet3DSImporter::ParseMainChunk() {
|
||||||
|
|
||||||
case Discreet3DS::CHUNK_PRJ:
|
case Discreet3DS::CHUNK_PRJ:
|
||||||
bIsPrj = true;
|
bIsPrj = true;
|
||||||
|
break;
|
||||||
case Discreet3DS::CHUNK_MAIN:
|
case Discreet3DS::CHUNK_MAIN:
|
||||||
ParseEditorChunk();
|
ParseEditorChunk();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -61,12 +61,12 @@ namespace Assimp {
|
||||||
void AMFImporter::ParseNode_Mesh(XmlNode &node) {
|
void AMFImporter::ParseNode_Mesh(XmlNode &node) {
|
||||||
AMFNodeElementBase *ne = nullptr;
|
AMFNodeElementBase *ne = nullptr;
|
||||||
|
|
||||||
// create new mesh object.
|
|
||||||
ne = new AMFMesh(mNodeElement_Cur);
|
|
||||||
// Check for child nodes
|
// Check for child nodes
|
||||||
if (0 != ASSIMP_stricmp(node.name(), "mesh")) {
|
if (0 != ASSIMP_stricmp(node.name(), "mesh")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// create new mesh object.
|
||||||
|
ne = new AMFMesh(mNodeElement_Cur);
|
||||||
bool found_verts = false, found_volumes = false;
|
bool found_verts = false, found_volumes = false;
|
||||||
if (!node.empty()) {
|
if (!node.empty()) {
|
||||||
ParseHelper_Node_Enter(ne);
|
ParseHelper_Node_Enter(ne);
|
||||||
|
|
|
@ -165,15 +165,15 @@ void AMFImporter::ParseNode_Texture(XmlNode &node) {
|
||||||
std::string type = node.attribute("type").as_string();
|
std::string type = node.attribute("type").as_string();
|
||||||
bool tiled = node.attribute("tiled").as_bool();
|
bool tiled = node.attribute("tiled").as_bool();
|
||||||
|
|
||||||
// create new texture object.
|
|
||||||
AMFNodeElementBase *ne = new AMFTexture(mNodeElement_Cur);
|
|
||||||
|
|
||||||
AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
|
|
||||||
|
|
||||||
if (node.empty()) {
|
if (node.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create new texture object.
|
||||||
|
AMFNodeElementBase *ne = new AMFTexture(mNodeElement_Cur);
|
||||||
|
|
||||||
|
AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
|
||||||
|
|
||||||
std::string enc64_data = node.value();
|
std::string enc64_data = node.value();
|
||||||
// Check for child nodes
|
// Check for child nodes
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
|
||||||
|
|
||||||
#include "AssetLib/COB/COBLoader.h"
|
#include "AssetLib/COB/COBLoader.h"
|
||||||
#include "AssetLib/COB/COBScene.h"
|
#include "AssetLib/COB/COBScene.h"
|
||||||
#include "PostProcessing/ConvertToLHProcess.h"
|
#include "PostProcessing/ConvertToLHProcess.h"
|
||||||
|
@ -90,11 +91,15 @@ static const aiImporterDesc desc = {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
COBImporter::COBImporter() {}
|
COBImporter::COBImporter() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
COBImporter::~COBImporter() {}
|
COBImporter::~COBImporter() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// Returns whether the class can handle the format of the given file.
|
||||||
|
@ -466,8 +471,9 @@ void COBImporter::UnsupportedChunk_Ascii(LineSplitter &splitter, const ChunkInfo
|
||||||
// missing the next line.
|
// missing the next line.
|
||||||
splitter.get_stream().IncPtr(nfo.size);
|
splitter.get_stream().IncPtr(nfo.size);
|
||||||
splitter.swallow_next_increment();
|
splitter.swallow_next_increment();
|
||||||
} else
|
} else {
|
||||||
ThrowException(error);
|
ThrowException(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -790,25 +796,12 @@ void COBImporter::ReadBitM_Ascii(Scene & /*out*/, LineSplitter &splitter, const
|
||||||
if (nfo.version > 1) {
|
if (nfo.version > 1) {
|
||||||
return UnsupportedChunk_Ascii(splitter, nfo, "BitM");
|
return UnsupportedChunk_Ascii(splitter, nfo, "BitM");
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
"\nThumbNailHdrSize %ld"
|
|
||||||
"\nThumbHeader: %02hx 02hx %02hx "
|
|
||||||
"\nColorBufSize %ld"
|
|
||||||
"\nColorBufZipSize %ld"
|
|
||||||
"\nZippedThumbnail: %02hx 02hx %02hx "
|
|
||||||
*/
|
|
||||||
|
|
||||||
const unsigned int head = strtoul10((++splitter)[1]);
|
const unsigned int head = strtoul10((++splitter)[1]);
|
||||||
if (head != sizeof(Bitmap::BitmapHeader)) {
|
if (head != sizeof(Bitmap::BitmapHeader)) {
|
||||||
ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk");
|
ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*union {
|
|
||||||
Bitmap::BitmapHeader data;
|
|
||||||
char opaq[sizeof Bitmap::BitmapHeader()];
|
|
||||||
};*/
|
|
||||||
// ReadHexOctets(opaq,head,(++splitter)[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -884,7 +877,10 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
std::string type;
|
std::string type;
|
||||||
type += reader->GetI1(), type += reader->GetI1(), type += reader->GetI1(), type += reader->GetI1();
|
type += reader->GetI1();
|
||||||
|
type += reader->GetI1();
|
||||||
|
type += reader->GetI1();
|
||||||
|
type += reader->GetI1();
|
||||||
|
|
||||||
ChunkInfo nfo;
|
ChunkInfo nfo;
|
||||||
nfo.version = reader->GetI2() * 10;
|
nfo.version = reader->GetI2() * 10;
|
||||||
|
@ -906,14 +902,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
|
||||||
ReadCame_Binary(out, *reader, nfo);
|
ReadCame_Binary(out, *reader, nfo);
|
||||||
} else if (type == "Mat1") {
|
} else if (type == "Mat1") {
|
||||||
ReadMat1_Binary(out, *reader, nfo);
|
ReadMat1_Binary(out, *reader, nfo);
|
||||||
}
|
} else if (type == "Unit") {
|
||||||
/* else if (type == "Bone") {
|
|
||||||
ReadBone_Binary(out,*reader,nfo);
|
|
||||||
}
|
|
||||||
else if (type == "Chan") {
|
|
||||||
ReadChan_Binary(out,*reader,nfo);
|
|
||||||
}*/
|
|
||||||
else if (type == "Unit") {
|
|
||||||
ReadUnit_Binary(out, *reader, nfo);
|
ReadUnit_Binary(out, *reader, nfo);
|
||||||
} else if (type == "OLay") {
|
} else if (type == "OLay") {
|
||||||
// ignore layer index silently.
|
// ignore layer index silently.
|
||||||
|
@ -923,8 +912,9 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
|
||||||
return UnsupportedChunk_Binary(*reader, nfo, type.c_str());
|
return UnsupportedChunk_Binary(*reader, nfo, type.c_str());
|
||||||
} else if (type == "END ") {
|
} else if (type == "END ") {
|
||||||
return;
|
return;
|
||||||
} else
|
} else {
|
||||||
UnsupportedChunk_Binary(*reader, nfo, type.c_str());
|
UnsupportedChunk_Binary(*reader, nfo, type.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,7 +334,7 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
|
||||||
const std::string ¤tName = currentNode.name();
|
const std::string ¤tName = currentNode.name();
|
||||||
if (currentName == "unit") {
|
if (currentName == "unit") {
|
||||||
mUnitSize = 1.f;
|
mUnitSize = 1.f;
|
||||||
XmlParser::getFloatAttribute(node, "meter", mUnitSize);
|
XmlParser::getFloatAttribute(currentNode, "meter", mUnitSize);
|
||||||
} else if (currentName == "up_axis") {
|
} else if (currentName == "up_axis") {
|
||||||
std::string v;
|
std::string v;
|
||||||
if (!XmlParser::getValueAsString(currentNode, v)) {
|
if (!XmlParser::getValueAsString(currentNode, v)) {
|
||||||
|
@ -459,7 +459,6 @@ void ColladaParser::PostProcessRootAnimations() {
|
||||||
|
|
||||||
if (animation != mAnimationLibrary.end()) {
|
if (animation != mAnimationLibrary.end()) {
|
||||||
Animation *pSourceAnimation = animation->second;
|
Animation *pSourceAnimation = animation->second;
|
||||||
|
|
||||||
pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
|
pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1738,14 +1737,16 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
|
||||||
|
|
||||||
// and read all indices into a temporary array
|
// and read all indices into a temporary array
|
||||||
std::vector<size_t> indices;
|
std::vector<size_t> indices;
|
||||||
if (expectedPointCount > 0)
|
if (expectedPointCount > 0) {
|
||||||
indices.reserve(expectedPointCount * numOffsets);
|
indices.reserve(expectedPointCount * numOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
if (pNumPrimitives > 0) // It is possible to not contain any indices
|
// It is possible to not contain any indices
|
||||||
{
|
if (pNumPrimitives > 0) {
|
||||||
std::string v;
|
std::string v;
|
||||||
XmlParser::getValueAsString(node, v);
|
XmlParser::getValueAsString(node, v);
|
||||||
const char *content = v.c_str();
|
const char *content = v.c_str();
|
||||||
|
SkipSpacesAndLineEnd(&content);
|
||||||
while (*content != 0) {
|
while (*content != 0) {
|
||||||
// read a value.
|
// read a value.
|
||||||
// Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
|
// Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
|
||||||
|
@ -1772,21 +1773,24 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
|
||||||
// find the data for all sources
|
// find the data for all sources
|
||||||
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
|
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
|
||||||
InputChannel &input = *it;
|
InputChannel &input = *it;
|
||||||
if (input.mResolved)
|
if (input.mResolved) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// find accessor
|
// find accessor
|
||||||
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
|
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
|
||||||
// resolve accessor's data pointer as well, if necessary
|
// resolve accessor's data pointer as well, if necessary
|
||||||
const Accessor *acc = input.mResolved;
|
const Accessor *acc = input.mResolved;
|
||||||
if (!acc->mData)
|
if (!acc->mData) {
|
||||||
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
|
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// and the same for the per-index channels
|
// and the same for the per-index channels
|
||||||
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
|
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
|
||||||
InputChannel &input = *it;
|
InputChannel &input = *it;
|
||||||
if (input.mResolved)
|
if (input.mResolved) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore vertex pointer, it doesn't refer to an accessor
|
// ignore vertex pointer, it doesn't refer to an accessor
|
||||||
if (input.mType == IT_Vertex) {
|
if (input.mType == IT_Vertex) {
|
||||||
|
@ -1801,8 +1805,9 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
|
||||||
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
|
input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
|
||||||
// resolve accessor's data pointer as well, if necessary
|
// resolve accessor's data pointer as well, if necessary
|
||||||
const Accessor *acc = input.mResolved;
|
const Accessor *acc = input.mResolved;
|
||||||
if (!acc->mData)
|
if (!acc->mData) {
|
||||||
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
|
acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
|
// For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
|
||||||
|
@ -1884,11 +1889,13 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n
|
||||||
ai_assert((baseOffset + numOffsets - 1) < indices.size());
|
ai_assert((baseOffset + numOffsets - 1) < indices.size());
|
||||||
|
|
||||||
// extract per-vertex channels using the global per-vertex offset
|
// extract per-vertex channels using the global per-vertex offset
|
||||||
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it)
|
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
|
||||||
ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
|
ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
|
||||||
|
}
|
||||||
// and extract per-index channels using there specified offset
|
// and extract per-index channels using there specified offset
|
||||||
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
|
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
|
||||||
ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
|
ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
|
||||||
|
}
|
||||||
|
|
||||||
// store the vertex-data index for later assignment of bone vertex weights
|
// store the vertex-data index for later assignment of bone vertex weights
|
||||||
pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
|
pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
|
||||||
|
@ -1912,8 +1919,9 @@ void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset,
|
||||||
// Extracts a single object from an input channel and stores it in the appropriate mesh data array
|
// Extracts a single object from an input channel and stores it in the appropriate mesh data array
|
||||||
void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) {
|
void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) {
|
||||||
// ignore vertex referrer - we handle them that separate
|
// ignore vertex referrer - we handle them that separate
|
||||||
if (pInput.mType == IT_Vertex)
|
if (pInput.mType == IT_Vertex) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const Accessor &acc = *pInput.mResolved;
|
const Accessor &acc = *pInput.mResolved;
|
||||||
if (pLocalIndex >= acc.mCount) {
|
if (pLocalIndex >= acc.mCount) {
|
||||||
|
@ -1926,86 +1934,93 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
|
||||||
// assemble according to the accessors component sub-offset list. We don't care, yet,
|
// assemble according to the accessors component sub-offset list. We don't care, yet,
|
||||||
// what kind of object exactly we're extracting here
|
// what kind of object exactly we're extracting here
|
||||||
ai_real obj[4];
|
ai_real obj[4];
|
||||||
for (size_t c = 0; c < 4; ++c)
|
for (size_t c = 0; c < 4; ++c) {
|
||||||
obj[c] = dataObject[acc.mSubOffset[c]];
|
obj[c] = dataObject[acc.mSubOffset[c]];
|
||||||
|
}
|
||||||
|
|
||||||
// now we reinterpret it according to the type we're reading here
|
// now we reinterpret it according to the type we're reading here
|
||||||
switch (pInput.mType) {
|
switch (pInput.mType) {
|
||||||
case IT_Position: // ignore all position streams except 0 - there can be only one position
|
case IT_Position: // ignore all position streams except 0 - there can be only one position
|
||||||
if (pInput.mIndex == 0)
|
if (pInput.mIndex == 0) {
|
||||||
pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
||||||
else
|
} else {
|
||||||
ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
|
ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
|
||||||
break;
|
|
||||||
case IT_Normal:
|
|
||||||
// pad to current vertex count if necessary
|
|
||||||
if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1)
|
|
||||||
pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0));
|
|
||||||
|
|
||||||
// ignore all normal streams except 0 - there can be only one normal
|
|
||||||
if (pInput.mIndex == 0)
|
|
||||||
pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
|
||||||
else
|
|
||||||
ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
|
|
||||||
break;
|
|
||||||
case IT_Tangent:
|
|
||||||
// pad to current vertex count if necessary
|
|
||||||
if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1)
|
|
||||||
pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0));
|
|
||||||
|
|
||||||
// ignore all tangent streams except 0 - there can be only one tangent
|
|
||||||
if (pInput.mIndex == 0)
|
|
||||||
pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
|
||||||
else
|
|
||||||
ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
|
|
||||||
break;
|
|
||||||
case IT_Bitangent:
|
|
||||||
// pad to current vertex count if necessary
|
|
||||||
if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1)
|
|
||||||
pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1));
|
|
||||||
|
|
||||||
// ignore all bitangent streams except 0 - there can be only one bitangent
|
|
||||||
if (pInput.mIndex == 0)
|
|
||||||
pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
|
||||||
else
|
|
||||||
ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
|
|
||||||
break;
|
|
||||||
case IT_Texcoord:
|
|
||||||
// up to 4 texture coord sets are fine, ignore the others
|
|
||||||
if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
|
|
||||||
// pad to current vertex count if necessary
|
|
||||||
if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
|
|
||||||
pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(),
|
|
||||||
pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
|
|
||||||
|
|
||||||
pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
|
||||||
if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
|
|
||||||
pMesh.mNumUVComponents[pInput.mIndex] = 3;
|
|
||||||
} else {
|
|
||||||
ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IT_Color:
|
|
||||||
// up to 4 color sets are fine, ignore the others
|
|
||||||
if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) {
|
|
||||||
// pad to current vertex count if necessary
|
|
||||||
if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
|
|
||||||
pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(),
|
|
||||||
pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1));
|
|
||||||
|
|
||||||
aiColor4D result(0, 0, 0, 1);
|
|
||||||
for (size_t i = 0; i < pInput.mResolved->mSize; ++i) {
|
|
||||||
result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
|
|
||||||
}
|
}
|
||||||
pMesh.mColors[pInput.mIndex].push_back(result);
|
break;
|
||||||
} else {
|
case IT_Normal:
|
||||||
ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
|
// pad to current vertex count if necessary
|
||||||
}
|
if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1)
|
||||||
|
pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0));
|
||||||
|
|
||||||
break;
|
// ignore all normal streams except 0 - there can be only one normal
|
||||||
default:
|
if (pInput.mIndex == 0) {
|
||||||
// IT_Invalid and IT_Vertex
|
pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
||||||
ai_assert(false && "shouldn't ever get here");
|
} else {
|
||||||
|
ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IT_Tangent:
|
||||||
|
// pad to current vertex count if necessary
|
||||||
|
if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1)
|
||||||
|
pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0));
|
||||||
|
|
||||||
|
// ignore all tangent streams except 0 - there can be only one tangent
|
||||||
|
if (pInput.mIndex == 0) {
|
||||||
|
pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IT_Bitangent:
|
||||||
|
// pad to current vertex count if necessary
|
||||||
|
if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) {
|
||||||
|
pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore all bitangent streams except 0 - there can be only one bitangent
|
||||||
|
if (pInput.mIndex == 0) {
|
||||||
|
pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IT_Texcoord:
|
||||||
|
// up to 4 texture coord sets are fine, ignore the others
|
||||||
|
if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
|
||||||
|
// pad to current vertex count if necessary
|
||||||
|
if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
|
||||||
|
pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(),
|
||||||
|
pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
|
||||||
|
|
||||||
|
pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
|
||||||
|
if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) {
|
||||||
|
pMesh.mNumUVComponents[pInput.mIndex] = 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IT_Color:
|
||||||
|
// up to 4 color sets are fine, ignore the others
|
||||||
|
if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) {
|
||||||
|
// pad to current vertex count if necessary
|
||||||
|
if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
|
||||||
|
pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(),
|
||||||
|
pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1));
|
||||||
|
|
||||||
|
aiColor4D result(0, 0, 0, 1);
|
||||||
|
for (size_t i = 0; i < pInput.mResolved->mSize; ++i) {
|
||||||
|
result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
|
||||||
|
}
|
||||||
|
pMesh.mColors[pInput.mIndex].push_back(result);
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// IT_Invalid and IT_Vertex
|
||||||
|
ai_assert(false && "shouldn't ever get here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -656,7 +656,7 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( openings && ((sides_with_openings == 1 && sides_with_openings) || (sides_with_v_openings == 2 && sides_with_v_openings)) ) {
|
if( openings && (sides_with_openings == 1 || sides_with_v_openings == 2 ) ) {
|
||||||
IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
|
IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1189,20 +1189,9 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
||||||
TempMesh* profile_data = opening.profileMesh.get();
|
TempMesh* profile_data = opening.profileMesh.get();
|
||||||
bool is_2d_source = false;
|
bool is_2d_source = false;
|
||||||
if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) {
|
if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) {
|
||||||
|
if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
|
||||||
if(std::fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) {
|
profile_data = opening.profileMesh2D.get();
|
||||||
// horizontal extrusion
|
is_2d_source = true;
|
||||||
if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
|
|
||||||
profile_data = opening.profileMesh2D.get();
|
|
||||||
is_2d_source = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// vertical extrusion
|
|
||||||
if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
|
|
||||||
profile_data = opening.profileMesh2D.get();
|
|
||||||
is_2d_source = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<IfcVector3> profile_verts = profile_data->mVerts;
|
std::vector<IfcVector3> profile_verts = profile_data->mVerts;
|
||||||
|
|
|
@ -147,7 +147,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo
|
||||||
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
||||||
WriteMesh(pScene->mMeshes[ i ]);
|
WriteMesh(pScene->mMeshes[ i ]);
|
||||||
}
|
}
|
||||||
mOutput << EndSolidToken << name << endl;
|
mOutput << EndSolidToken << " " << name << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -383,6 +383,22 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
|
||||||
}
|
}
|
||||||
#endif // ASSIMP_BUILD_DEBUG
|
#endif // ASSIMP_BUILD_DEBUG
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
aiColor4D* GetVertexColorsForType(glTF2::Ref<glTF2::Accessor> input) {
|
||||||
|
float max = std::numeric_limits<T>::max();
|
||||||
|
aiColor4t<T>* colors;
|
||||||
|
input->ExtractData(colors);
|
||||||
|
auto output = new aiColor4D[input->count];
|
||||||
|
for (size_t i = 0; i < input->count; i++) {
|
||||||
|
output[i] = aiColor4D(
|
||||||
|
colors[i].r / max, colors[i].g / max,
|
||||||
|
colors[i].b / max, colors[i].a / max
|
||||||
|
);
|
||||||
|
}
|
||||||
|
delete[] colors;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes");
|
ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes");
|
||||||
std::vector<std::unique_ptr<aiMesh>> meshes;
|
std::vector<std::unique_ptr<aiMesh>> meshes;
|
||||||
|
@ -463,7 +479,17 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
|
||||||
"\" does not match the vertex count");
|
"\" does not match the vertex count");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
attr.color[c]->ExtractData(aim->mColors[c]);
|
|
||||||
|
auto componentType = attr.color[c]->componentType;
|
||||||
|
if (componentType == glTF2::ComponentType_FLOAT) {
|
||||||
|
attr.color[c]->ExtractData(aim->mColors[c]);
|
||||||
|
} else {
|
||||||
|
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
|
||||||
|
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
|
||||||
|
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
|
||||||
|
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
|
for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
|
||||||
if (!attr.texcoord[tc]) {
|
if (!attr.texcoord[tc]) {
|
||||||
|
|
|
@ -665,6 +665,10 @@ if (NOT ASSIMP_NO_EXPORT)
|
||||||
AssetLib/3MF/D3MFExporter.h
|
AssetLib/3MF/D3MFExporter.h
|
||||||
AssetLib/3MF/D3MFExporter.cpp)
|
AssetLib/3MF/D3MFExporter.cpp)
|
||||||
|
|
||||||
|
ADD_ASSIMP_EXPORTER( PBRT
|
||||||
|
Pbrt/PbrtExporter.h
|
||||||
|
Pbrt/PbrtExporter.cpp)
|
||||||
|
|
||||||
ADD_ASSIMP_EXPORTER( ASSJSON
|
ADD_ASSIMP_EXPORTER( ASSJSON
|
||||||
AssetLib/Assjson/cencode.c
|
AssetLib/Assjson/cencode.c
|
||||||
AssetLib/Assjson/cencode.h
|
AssetLib/Assjson/cencode.h
|
||||||
|
|
|
@ -60,19 +60,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
const std::wstring wdummy;
|
||||||
|
|
||||||
static std::wstring Utf8ToWide(const char *in) {
|
static std::wstring Utf8ToWide(const char *in) {
|
||||||
|
if (nullptr == in) {
|
||||||
|
return wdummy;
|
||||||
|
}
|
||||||
int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
|
int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
|
||||||
// size includes terminating null; std::wstring adds null automatically
|
// size includes terminating null; std::wstring adds null automatically
|
||||||
std::wstring out(static_cast<size_t>(size) - 1, L'\0');
|
std::wstring out(static_cast<size_t>(size) - 1, L'\0');
|
||||||
MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
|
MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string dummy;
|
||||||
|
|
||||||
static std::string WideToUtf8(const wchar_t *in) {
|
static std::string WideToUtf8(const wchar_t *in) {
|
||||||
|
if (nullptr == in) {
|
||||||
|
return dummy;
|
||||||
|
}
|
||||||
int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
|
int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
|
||||||
// size includes terminating null; std::string adds null automatically
|
// size includes terminating null; std::string adds null automatically
|
||||||
std::string out(static_cast<size_t>(size) - 1, '\0');
|
std::string out(static_cast<size_t>(size) - 1, '\0');
|
||||||
WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
|
WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -104,7 +117,12 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) {
|
||||||
ai_assert(strMode != nullptr);
|
ai_assert(strMode != nullptr);
|
||||||
FILE *file;
|
FILE *file;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
|
std::wstring name = Utf8ToWide(strFile);
|
||||||
|
if (name.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str());
|
||||||
#else
|
#else
|
||||||
file = ::fopen(strFile, strMode);
|
file = ::fopen(strFile, strMode);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -138,6 +138,9 @@ void ExportSceneM3DA(const char*, IOSystem*, const aiScene*, const ExportPropert
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
||||||
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
|
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
void ExportScenePbrt(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||||
|
#endif
|
||||||
|
|
||||||
static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
|
static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
|
||||||
(void)exporters;
|
(void)exporters;
|
||||||
|
@ -221,6 +224,10 @@ static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporte
|
||||||
exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
|
exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
exporters.push_back(Exporter::ExportFormatEntry("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType));
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
||||||
exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
|
exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2020, assimp team
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -52,8 +51,12 @@ void ScenePreprocessor::ProcessScene() {
|
||||||
ai_assert(scene != nullptr);
|
ai_assert(scene != nullptr);
|
||||||
|
|
||||||
// Process all meshes
|
// Process all meshes
|
||||||
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
|
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
|
||||||
|
if (nullptr == scene->mMeshes[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ProcessMesh(scene->mMeshes[i]);
|
ProcessMesh(scene->mMeshes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// - nothing to do for nodes for the moment
|
// - nothing to do for nodes for the moment
|
||||||
// - nothing to do for textures for the moment
|
// - nothing to do for textures for the moment
|
||||||
|
@ -61,8 +64,12 @@ void ScenePreprocessor::ProcessScene() {
|
||||||
// - nothing to do for cameras for the moment
|
// - nothing to do for cameras for the moment
|
||||||
|
|
||||||
// Process all animations
|
// Process all animations
|
||||||
for (unsigned int i = 0; i < scene->mNumAnimations; ++i)
|
for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
|
||||||
|
if (nullptr == scene->mAnimations[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ProcessAnimation(scene->mAnimations[i]);
|
ProcessAnimation(scene->mAnimations[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a default material if none was specified
|
// Generate a default material if none was specified
|
||||||
if (!scene->mNumMaterials && scene->mNumMeshes) {
|
if (!scene->mNumMaterials && scene->mNumMeshes) {
|
||||||
|
|
|
@ -152,7 +152,7 @@ zlib_filefunc_def IOSystem2Unzip::get(IOSystem *pIOHandler) {
|
||||||
mapping.ztell_file = (tell_file_func)tell;
|
mapping.ztell_file = (tell_file_func)tell;
|
||||||
mapping.zseek_file = (seek_file_func)seek;
|
mapping.zseek_file = (seek_file_func)seek;
|
||||||
mapping.zclose_file = (close_file_func)close;
|
mapping.zclose_file = (close_file_func)close;
|
||||||
mapping.zerror_file = (error_file_func)testerror;
|
mapping.zerror_file = testerror;
|
||||||
|
|
||||||
mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
|
mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,949 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
with or without modification, are permitted provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other
|
||||||
|
materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the assimp team, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior
|
||||||
|
written permission of the assimp team.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
|
||||||
|
Material improvements:
|
||||||
|
- don't export embedded textures that we're not going to use
|
||||||
|
- diffuse roughness
|
||||||
|
- what is with the uv mapping, uv transform not coming through??
|
||||||
|
- metal? glass? mirror? detect these better?
|
||||||
|
- eta/k from RGB?
|
||||||
|
- emissive textures: warn at least
|
||||||
|
|
||||||
|
Other:
|
||||||
|
- use aiProcess_GenUVCoords if needed to handle spherical/planar uv mapping?
|
||||||
|
- don't build up a big string in memory but write directly to a file
|
||||||
|
- aiProcess_Triangulate meshes to get triangles only?
|
||||||
|
- animation (allow specifying a time)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
|
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
|
||||||
|
#include "PbrtExporter.h"
|
||||||
|
|
||||||
|
#include <assimp/version.h>
|
||||||
|
#include <assimp/DefaultIOSystem.h>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
#include <assimp/Exporter.hpp>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
#include <assimp/StreamWriter.h>
|
||||||
|
#include <assimp/Exceptional.h>
|
||||||
|
#include <assimp/material.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/mesh.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
void ExportScenePbrt (
|
||||||
|
const char* pFile,
|
||||||
|
IOSystem* pIOSystem,
|
||||||
|
const aiScene* pScene,
|
||||||
|
const ExportProperties* /*pProperties*/
|
||||||
|
){
|
||||||
|
std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
|
||||||
|
std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
|
||||||
|
|
||||||
|
// initialize the exporter
|
||||||
|
PbrtExporter exporter(pScene, pIOSystem, path, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace Assimp
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
PbrtExporter::PbrtExporter (
|
||||||
|
const aiScene* pScene, IOSystem* pIOSystem,
|
||||||
|
const std::string path, const std::string file)
|
||||||
|
: mScene(pScene),
|
||||||
|
mIOSystem(pIOSystem),
|
||||||
|
mPath(path),
|
||||||
|
mFile(file)
|
||||||
|
{
|
||||||
|
// Export embedded textures.
|
||||||
|
if (mScene->mNumTextures > 0)
|
||||||
|
if (!mIOSystem->CreateDirectory("textures"))
|
||||||
|
throw DeadlyExportError("Could not create textures/ directory.");
|
||||||
|
for (unsigned int i = 0; i < mScene->mNumTextures; ++i) {
|
||||||
|
aiTexture* tex = mScene->mTextures[i];
|
||||||
|
std::string fn = CleanTextureFilename(tex->mFilename, false);
|
||||||
|
std::cerr << "Writing embedded texture: " << tex->mFilename.C_Str() << " -> "
|
||||||
|
<< fn << "\n";
|
||||||
|
|
||||||
|
std::unique_ptr<IOStream> outfile(mIOSystem->Open(fn, "wb"));
|
||||||
|
if (!outfile) {
|
||||||
|
throw DeadlyExportError("could not open output texture file: " + fn);
|
||||||
|
}
|
||||||
|
if (tex->mHeight == 0) {
|
||||||
|
// It's binary data
|
||||||
|
outfile->Write(tex->pcData, tex->mWidth, 1);
|
||||||
|
} else {
|
||||||
|
std::cerr << fn << ": TODO handle uncompressed embedded textures.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Debugging: print the full node hierarchy
|
||||||
|
std::function<void(aiNode*, int)> visitNode;
|
||||||
|
visitNode = [&](aiNode* node, int depth) {
|
||||||
|
for (int i = 0; i < depth; ++i) std::cerr << " ";
|
||||||
|
std::cerr << node->mName.C_Str() << "\n";
|
||||||
|
for (int i = 0; i < node->mNumChildren; ++i)
|
||||||
|
visitNode(node->mChildren[i], depth + 1);
|
||||||
|
};
|
||||||
|
visitNode(mScene->mRootNode, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
|
||||||
|
|
||||||
|
// Write everything out
|
||||||
|
WriteMetaData();
|
||||||
|
WriteCameras();
|
||||||
|
WriteWorldDefinition();
|
||||||
|
|
||||||
|
// And write the file to disk...
|
||||||
|
std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath,"wt"));
|
||||||
|
if (!outfile) {
|
||||||
|
throw DeadlyExportError("could not open output .pbrt file: " + std::string(mFile));
|
||||||
|
}
|
||||||
|
outfile->Write(mOutput.str().c_str(), mOutput.str().length(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
PbrtExporter::~PbrtExporter() {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteMetaData() {
|
||||||
|
mOutput << "#############################\n";
|
||||||
|
mOutput << "# Scene metadata:\n";
|
||||||
|
|
||||||
|
aiMetadata* pMetaData = mScene->mMetaData;
|
||||||
|
for (unsigned int i = 0; i < pMetaData->mNumProperties; i++) {
|
||||||
|
mOutput << "# - ";
|
||||||
|
mOutput << pMetaData->mKeys[i].C_Str() << " :";
|
||||||
|
switch(pMetaData->mValues[i].mType) {
|
||||||
|
case AI_BOOL : {
|
||||||
|
mOutput << " ";
|
||||||
|
if (*static_cast<bool*>(pMetaData->mValues[i].mData))
|
||||||
|
mOutput << "TRUE\n";
|
||||||
|
else
|
||||||
|
mOutput << "FALSE\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AI_INT32 : {
|
||||||
|
mOutput << " " <<
|
||||||
|
*static_cast<int32_t*>(pMetaData->mValues[i].mData) <<
|
||||||
|
std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AI_UINT64 :
|
||||||
|
mOutput << " " <<
|
||||||
|
*static_cast<uint64_t*>(pMetaData->mValues[i].mData) <<
|
||||||
|
std::endl;
|
||||||
|
break;
|
||||||
|
case AI_FLOAT :
|
||||||
|
mOutput << " " <<
|
||||||
|
*static_cast<float*>(pMetaData->mValues[i].mData) <<
|
||||||
|
std::endl;
|
||||||
|
break;
|
||||||
|
case AI_DOUBLE :
|
||||||
|
mOutput << " " <<
|
||||||
|
*static_cast<double*>(pMetaData->mValues[i].mData) <<
|
||||||
|
std::endl;
|
||||||
|
break;
|
||||||
|
case AI_AISTRING : {
|
||||||
|
aiString* value =
|
||||||
|
static_cast<aiString*>(pMetaData->mValues[i].mData);
|
||||||
|
std::string svalue = value->C_Str();
|
||||||
|
std::size_t found = svalue.find_first_of("\n");
|
||||||
|
mOutput << "\n";
|
||||||
|
while (found != std::string::npos) {
|
||||||
|
mOutput << "# " << svalue.substr(0, found) << "\n";
|
||||||
|
svalue = svalue.substr(found + 1);
|
||||||
|
found = svalue.find_first_of("\n");
|
||||||
|
}
|
||||||
|
mOutput << "# " << svalue << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AI_AIVECTOR3D :
|
||||||
|
// TODO
|
||||||
|
mOutput << " Vector3D (unable to print)\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// AI_META_MAX and FORCE_32BIT
|
||||||
|
mOutput << " META_MAX or FORCE_32Bit (unable to print)\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteCameras() {
|
||||||
|
mOutput << "\n";
|
||||||
|
mOutput << "###############################\n";
|
||||||
|
mOutput << "# Cameras (" << mScene->mNumCameras << ") total\n\n";
|
||||||
|
|
||||||
|
if (mScene->mNumCameras == 0) {
|
||||||
|
std::cerr << "Warning: No cameras found in scene file.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mScene->mNumCameras > 1) {
|
||||||
|
std::cerr << "Multiple cameras found in scene file; defaulting to first one specified.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mScene->mNumCameras; i++) {
|
||||||
|
WriteCamera(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aiMatrix4x4 PbrtExporter::GetNodeTransform(const aiString &name) const {
|
||||||
|
aiMatrix4x4 m;
|
||||||
|
auto node = mScene->mRootNode->FindNode(name);
|
||||||
|
if (!node) {
|
||||||
|
std::cerr << '"' << name.C_Str() << "\": node not found in scene tree.\n";
|
||||||
|
throw DeadlyExportError("Could not find node");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (node) {
|
||||||
|
m = node->mTransformation * m;
|
||||||
|
node = node->mParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PbrtExporter::TransformAsString(const aiMatrix4x4 &m) {
|
||||||
|
// Transpose on the way out to match pbrt's expected layout (sanity
|
||||||
|
// check: the translation component should be the last 3 entries
|
||||||
|
// before a '1' as the final entry in the matrix, assuming it's
|
||||||
|
// non-projective.)
|
||||||
|
std::stringstream s;
|
||||||
|
s << m.a1 << " " << m.b1 << " " << m.c1 << " " << m.d1 << " "
|
||||||
|
<< m.a2 << " " << m.b2 << " " << m.c2 << " " << m.d2 << " "
|
||||||
|
<< m.a3 << " " << m.b3 << " " << m.c3 << " " << m.d3 << " "
|
||||||
|
<< m.a4 << " " << m.b4 << " " << m.c4 << " " << m.d4;
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteCamera(int i) {
|
||||||
|
auto camera = mScene->mCameras[i];
|
||||||
|
bool cameraActive = i == 0;
|
||||||
|
|
||||||
|
mOutput << "# - Camera " << i+1 << ": "
|
||||||
|
<< camera->mName.C_Str() << "\n";
|
||||||
|
|
||||||
|
// Get camera aspect ratio
|
||||||
|
float aspect = camera->mAspect;
|
||||||
|
if (aspect == 0) {
|
||||||
|
aspect = 4.f/3.f;
|
||||||
|
mOutput << "# - Aspect ratio : 1.33333 (no aspect found, defaulting to 4/3)\n";
|
||||||
|
} else {
|
||||||
|
mOutput << "# - Aspect ratio : " << aspect << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Film xres and yres
|
||||||
|
int xres = 1920;
|
||||||
|
int yres = (int)round(xres/aspect);
|
||||||
|
|
||||||
|
// Print Film for this camera
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << "Film \"rgb\" \"string filename\" \"" << mFile << ".exr\"\n";
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << " \"integer xresolution\" [" << xres << "]\n";
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << " \"integer yresolution\" [" << yres << "]\n";
|
||||||
|
|
||||||
|
// Get camera fov
|
||||||
|
float hfov = AI_RAD_TO_DEG(camera->mHorizontalFOV);
|
||||||
|
float fov = (aspect >= 1.0) ? hfov : (hfov * aspect);
|
||||||
|
if (fov < 5) {
|
||||||
|
std::cerr << fov << ": suspiciously low field of view specified by camera. Setting to 45 degrees.\n";
|
||||||
|
fov = 45;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get camera transform
|
||||||
|
aiMatrix4x4 worldFromCamera = GetNodeTransform(camera->mName);
|
||||||
|
|
||||||
|
// Print Camera LookAt
|
||||||
|
auto position = worldFromCamera * camera->mPosition;
|
||||||
|
auto lookAt = worldFromCamera * (camera->mPosition + camera->mLookAt);
|
||||||
|
aiMatrix3x3 worldFromCamera3(worldFromCamera);
|
||||||
|
auto up = worldFromCamera3 * camera->mUp;
|
||||||
|
up.Normalize();
|
||||||
|
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << "Scale -1 1 1\n"; // right handed -> left handed
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << "LookAt "
|
||||||
|
<< position.x << " " << position.y << " " << position.z << "\n";
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << " "
|
||||||
|
<< lookAt.x << " " << lookAt.y << " " << lookAt.z << "\n";
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << " "
|
||||||
|
<< up.x << " " << up.y << " " << up.z << "\n";
|
||||||
|
|
||||||
|
// Print camera descriptor
|
||||||
|
if (!cameraActive)
|
||||||
|
mOutput << "# ";
|
||||||
|
mOutput << "Camera \"perspective\" \"float fov\" " << "[" << fov << "]\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteWorldDefinition() {
|
||||||
|
// Figure out which meshes are referenced multiple times; those will be
|
||||||
|
// emitted as object instances and the rest will be emitted directly.
|
||||||
|
std::map<int, int> meshUses;
|
||||||
|
std::function<void(aiNode*)> visitNode;
|
||||||
|
visitNode = [&](aiNode* node) {
|
||||||
|
for (unsigned int i = 0; i < node->mNumMeshes; ++i)
|
||||||
|
++meshUses[node->mMeshes[i]];
|
||||||
|
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
||||||
|
visitNode(node->mChildren[i]);
|
||||||
|
};
|
||||||
|
visitNode(mScene->mRootNode);
|
||||||
|
int nInstanced = 0, nUnused = 0;
|
||||||
|
for (const auto &u : meshUses) {
|
||||||
|
if (u.second == 0) ++nUnused;
|
||||||
|
else if (u.second > 1) ++nInstanced;
|
||||||
|
}
|
||||||
|
std::cerr << nInstanced << " / " << mScene->mNumMeshes << " meshes instanced.\n";
|
||||||
|
if (nUnused)
|
||||||
|
std::cerr << nUnused << " meshes defined but not used in scene.\n";
|
||||||
|
|
||||||
|
mOutput << "WorldBegin\n";
|
||||||
|
|
||||||
|
WriteLights();
|
||||||
|
WriteTextures();
|
||||||
|
WriteMaterials();
|
||||||
|
|
||||||
|
// Object instance definitions
|
||||||
|
mOutput << "# Object instance definitions\n\n";
|
||||||
|
for (const auto &mu : meshUses) {
|
||||||
|
if (mu.second > 1) {
|
||||||
|
WriteInstanceDefinition(mu.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mOutput << "# Geometry\n\n";
|
||||||
|
aiMatrix4x4 worldFromObject;
|
||||||
|
WriteGeometricObjects(mScene->mRootNode, worldFromObject, meshUses);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteTextures() {
|
||||||
|
mOutput << "###################\n";
|
||||||
|
mOutput << "# Textures\n\n";
|
||||||
|
|
||||||
|
C_STRUCT aiString path;
|
||||||
|
aiTextureMapping mapping;
|
||||||
|
unsigned int uvIndex;
|
||||||
|
ai_real blend;
|
||||||
|
aiTextureOp op;
|
||||||
|
aiTextureMapMode mapMode[3];
|
||||||
|
|
||||||
|
// For every material in the scene,
|
||||||
|
for (unsigned int m = 0 ; m < mScene->mNumMaterials; m++) {
|
||||||
|
auto material = mScene->mMaterials[m];
|
||||||
|
// Parse through all texture types,
|
||||||
|
for (int tt = 1; tt <= aiTextureType_UNKNOWN; tt++) {
|
||||||
|
int ttCount = material->GetTextureCount(aiTextureType(tt));
|
||||||
|
// ... and get every texture
|
||||||
|
for (int t = 0; t < ttCount; t++) {
|
||||||
|
// TODO write out texture specifics
|
||||||
|
// TODO UV transforms may be material specific
|
||||||
|
// so those may need to be baked into unique tex name
|
||||||
|
if (material->GetTexture(aiTextureType(tt), t, &path, &mapping,
|
||||||
|
&uvIndex, &blend, &op, mapMode) != AI_SUCCESS) {
|
||||||
|
std::cerr << "Error getting texture! " << m << " " << tt << " " << t << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string filename = CleanTextureFilename(path);
|
||||||
|
|
||||||
|
if (uvIndex != 0)
|
||||||
|
std::cerr << "Warning: texture \"" << filename << "\" uses uv set #" <<
|
||||||
|
uvIndex << " but the pbrt converter only exports uv set 0.\n";
|
||||||
|
#if 0
|
||||||
|
if (op != aiTextureOp_Multiply)
|
||||||
|
std::cerr << "Warning: unexpected texture op " << (int)op <<
|
||||||
|
" encountered for texture \"" <<
|
||||||
|
filename << "\". The resulting scene may have issues...\n";
|
||||||
|
if (blend != 1)
|
||||||
|
std::cerr << "Blend value of " << blend << " found for texture \"" << filename
|
||||||
|
<< "\" but not handled in converter.\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string mappingString;
|
||||||
|
#if 0
|
||||||
|
if (mapMode[0] != mapMode[1])
|
||||||
|
std::cerr << "Different texture boundary mode for u and v for texture \"" <<
|
||||||
|
filename << "\". Using u for both.\n";
|
||||||
|
switch (mapMode[0]) {
|
||||||
|
case aiTextureMapMode_Wrap:
|
||||||
|
// pbrt's default
|
||||||
|
break;
|
||||||
|
case aiTextureMapMode_Clamp:
|
||||||
|
mappingString = "\"string wrap\" \"clamp\"";
|
||||||
|
break;
|
||||||
|
case aiTextureMapMode_Decal:
|
||||||
|
std::cerr << "Decal texture boundary mode not supported by pbrt for texture \"" <<
|
||||||
|
filename << "\"\n";
|
||||||
|
break;
|
||||||
|
case aiTextureMapMode_Mirror:
|
||||||
|
std::cerr << "Mirror texture boundary mode not supported by pbrt for texture \"" <<
|
||||||
|
filename << "\"\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Unexpected map mode " << (int)mapMode[0] << " for texture \"" <<
|
||||||
|
filename << "\"\n";
|
||||||
|
//throw DeadlyExportError("Unexpected aiTextureMapMode");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
aiUVTransform uvTransform;
|
||||||
|
if (material->Get(AI_MATKEY_TEXTURE(tt, t), uvTransform) == AI_SUCCESS) {
|
||||||
|
mOutput << "# UV transform " << uvTransform.mTranslation.x << " "
|
||||||
|
<< uvTransform.mTranslation.y << " " << uvTransform.mScaling.x << " "
|
||||||
|
<< uvTransform.mScaling.y << " " << uvTransform.mRotation << "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string texName, texType, texOptions;
|
||||||
|
if (aiTextureType(tt) == aiTextureType_SHININESS ||
|
||||||
|
aiTextureType(tt) == aiTextureType_OPACITY ||
|
||||||
|
aiTextureType(tt) == aiTextureType_HEIGHT ||
|
||||||
|
aiTextureType(tt) == aiTextureType_DISPLACEMENT ||
|
||||||
|
aiTextureType(tt) == aiTextureType_METALNESS ||
|
||||||
|
aiTextureType(tt) == aiTextureType_DIFFUSE_ROUGHNESS) {
|
||||||
|
texType = "float";
|
||||||
|
texName = std::string("float:") + RemoveSuffix(filename);
|
||||||
|
|
||||||
|
if (aiTextureType(tt) == aiTextureType_SHININESS) {
|
||||||
|
texOptions = " \"bool invert\" true\n";
|
||||||
|
texName += "_Roughness";
|
||||||
|
}
|
||||||
|
} else if (aiTextureType(tt) == aiTextureType_DIFFUSE ||
|
||||||
|
aiTextureType(tt) == aiTextureType_BASE_COLOR) {
|
||||||
|
texType = "spectrum";
|
||||||
|
texName = std::string("rgb:") + RemoveSuffix(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't export textures we're not actually going to use...
|
||||||
|
if (texName.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (mTextureSet.find(texName) == mTextureSet.end()) {
|
||||||
|
mOutput << "Texture \"" << texName << "\" \"" << texType << "\" \"imagemap\"\n"
|
||||||
|
<< texOptions
|
||||||
|
<< " \"string filename\" \"" << filename << "\" " << mappingString << '\n';
|
||||||
|
mTextureSet.insert(texName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also emit a float version for use with alpha testing...
|
||||||
|
if ((aiTextureType(tt) == aiTextureType_DIFFUSE ||
|
||||||
|
aiTextureType(tt) == aiTextureType_BASE_COLOR) &&
|
||||||
|
TextureHasAlphaMask(filename)) {
|
||||||
|
texType = "float";
|
||||||
|
texName = std::string("alpha:") + filename;
|
||||||
|
if (mTextureSet.find(texName) == mTextureSet.end()) {
|
||||||
|
mOutput << "Texture \"" << texName << "\" \"" << texType << "\" \"imagemap\"\n"
|
||||||
|
<< " \"string filename\" \"" << filename << "\" " << mappingString << '\n';
|
||||||
|
mTextureSet.insert(texName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PbrtExporter::TextureHasAlphaMask(const std::string &filename) {
|
||||||
|
// TODO: STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
|
||||||
|
// quick return if it's 3
|
||||||
|
|
||||||
|
int xSize, ySize, nComponents;
|
||||||
|
unsigned char *data = stbi_load(filename.c_str(), &xSize, &ySize, &nComponents, 0);
|
||||||
|
if (!data) {
|
||||||
|
std::cerr << filename << ": unable to load texture and check for alpha mask in texture. "
|
||||||
|
"Geometry will not be alpha masked with this texture.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasMask = false;
|
||||||
|
switch (nComponents) {
|
||||||
|
case 1:
|
||||||
|
for (int i = 0; i < xSize * ySize; ++i)
|
||||||
|
if (data[i] != 255) {
|
||||||
|
hasMask = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
for (int y = 0; y < ySize; ++y)
|
||||||
|
for (int x = 0; x < xSize; ++x)
|
||||||
|
if (data[2 * (x + y * xSize) + 1] != 255) {
|
||||||
|
hasMask = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
for (int y = 0; y < ySize; ++y)
|
||||||
|
for (int x = 0; x < xSize; ++x)
|
||||||
|
if (data[4 * (x + y * xSize) + 3] != 255) {
|
||||||
|
hasMask = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << filename << ": unexpected number of image channels, " <<
|
||||||
|
nComponents << ".\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free(data);
|
||||||
|
return hasMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteMaterials() {
|
||||||
|
mOutput << "\n";
|
||||||
|
mOutput << "####################\n";
|
||||||
|
mOutput << "# Materials (" << mScene->mNumMaterials << ") total\n\n";
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mScene->mNumMaterials; i++) {
|
||||||
|
WriteMaterial(i);
|
||||||
|
}
|
||||||
|
mOutput << "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteMaterial(int m) {
|
||||||
|
aiMaterial* material = mScene->mMaterials[m];
|
||||||
|
|
||||||
|
// get material name
|
||||||
|
auto materialName = material->GetName();
|
||||||
|
mOutput << std::endl << "# - Material " << m+1 << ": " << materialName.C_Str() << "\n";
|
||||||
|
|
||||||
|
// Print out number of properties
|
||||||
|
mOutput << "# - Number of Material Properties: " << material->mNumProperties << "\n";
|
||||||
|
|
||||||
|
// Print out texture type counts
|
||||||
|
mOutput << "# - Non-Zero Texture Type Counts: ";
|
||||||
|
for (int i = 1; i <= aiTextureType_UNKNOWN; i++) {
|
||||||
|
int count = material->GetTextureCount(aiTextureType(i));
|
||||||
|
if (count > 0)
|
||||||
|
mOutput << TextureTypeToString(aiTextureType(i)) << ": " << count << " ";
|
||||||
|
}
|
||||||
|
mOutput << "\n";
|
||||||
|
|
||||||
|
auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; };
|
||||||
|
auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; };
|
||||||
|
|
||||||
|
aiColor3D diffuse, specular, transparency;
|
||||||
|
bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS &&
|
||||||
|
!White(diffuse));
|
||||||
|
bool constantSpecular = (material->Get(AI_MATKEY_COLOR_SPECULAR, specular) == AI_SUCCESS &&
|
||||||
|
!White(specular));
|
||||||
|
bool constantTransparency = (material->Get(AI_MATKEY_COLOR_TRANSPARENT, transparency) == AI_SUCCESS &&
|
||||||
|
!Black(transparency));
|
||||||
|
|
||||||
|
float opacity, shininess, shininessStrength, eta;
|
||||||
|
bool constantOpacity = (material->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS &&
|
||||||
|
opacity != 0);
|
||||||
|
bool constantShininess = material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS;
|
||||||
|
bool constantShininessStrength = material->Get(AI_MATKEY_SHININESS_STRENGTH, shininessStrength) == AI_SUCCESS;
|
||||||
|
bool constantEta = (material->Get(AI_MATKEY_REFRACTI, eta) == AI_SUCCESS &&
|
||||||
|
eta != 1);
|
||||||
|
|
||||||
|
mOutput << "# - Constants: diffuse " << constantDiffuse << " specular " << constantSpecular <<
|
||||||
|
" transprency " << constantTransparency << " opacity " << constantOpacity <<
|
||||||
|
" shininess " << constantShininess << " shininess strength " << constantShininessStrength <<
|
||||||
|
" eta " << constantEta << "\n";
|
||||||
|
|
||||||
|
aiString roughnessMap;
|
||||||
|
if (material->Get(AI_MATKEY_TEXTURE_SHININESS(0), roughnessMap) == AI_SUCCESS) {
|
||||||
|
std::string roughnessTexture = std::string("float:") +
|
||||||
|
RemoveSuffix(CleanTextureFilename(roughnessMap)) + "_Roughness";
|
||||||
|
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
|
||||||
|
<< " \"string type\" \"coateddiffuse\"\n"
|
||||||
|
<< " \"texture roughness\" \"" << roughnessTexture << "\"\n";
|
||||||
|
} else if (constantShininess) {
|
||||||
|
// Assume plastic for now at least
|
||||||
|
float roughness = std::max(0.f, 1.f - shininess);
|
||||||
|
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
|
||||||
|
<< " \"string type\" \"coateddiffuse\"\n"
|
||||||
|
<< " \"float roughness\" " << roughness << "\n";
|
||||||
|
} else
|
||||||
|
// Diffuse
|
||||||
|
mOutput << "MakeNamedMaterial \"" << materialName.C_Str() << "\""
|
||||||
|
<< " \"string type\" \"diffuse\"\n";
|
||||||
|
|
||||||
|
aiString diffuseTexture;
|
||||||
|
if (material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), diffuseTexture) == AI_SUCCESS)
|
||||||
|
mOutput << " \"texture reflectance\" \"rgb:" << RemoveSuffix(CleanTextureFilename(diffuseTexture)) << "\"\n";
|
||||||
|
else
|
||||||
|
mOutput << " \"rgb reflectance\" [ " << diffuse.r << " " << diffuse.g <<
|
||||||
|
" " << diffuse.b << " ]\n";
|
||||||
|
|
||||||
|
aiString displacementTexture, normalMap;
|
||||||
|
if (material->Get(AI_MATKEY_TEXTURE_NORMALS(0), displacementTexture) == AI_SUCCESS)
|
||||||
|
mOutput << " \"string normalmap\" \"" << CleanTextureFilename(displacementTexture) << "\"\n";
|
||||||
|
else if (material->Get(AI_MATKEY_TEXTURE_HEIGHT(0), displacementTexture) == AI_SUCCESS)
|
||||||
|
mOutput << " \"texture displacement\" \"float:" <<
|
||||||
|
RemoveSuffix(CleanTextureFilename(displacementTexture)) << "\"\n";
|
||||||
|
else if (material->Get(AI_MATKEY_TEXTURE_DISPLACEMENT(0), displacementTexture) == AI_SUCCESS)
|
||||||
|
mOutput << " \"texture displacement\" \"float:" <<
|
||||||
|
RemoveSuffix(CleanTextureFilename(displacementTexture)) << "\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PbrtExporter::CleanTextureFilename(const aiString &f, bool rewriteExtension) const {
|
||||||
|
std::string fn = f.C_Str();
|
||||||
|
// Remove directory name
|
||||||
|
size_t offset = fn.find_last_of("/\\");
|
||||||
|
if (offset != std::string::npos) {
|
||||||
|
fn.erase(0, offset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect all textures in textures
|
||||||
|
fn = std::string("textures") + mIOSystem->getOsSeparator() + fn;
|
||||||
|
|
||||||
|
// Rewrite extension for unsupported file formats.
|
||||||
|
if (rewriteExtension) {
|
||||||
|
offset = fn.rfind('.');
|
||||||
|
if (offset != std::string::npos) {
|
||||||
|
std::string extension = fn;
|
||||||
|
extension.erase(0, offset + 1);
|
||||||
|
std::transform(extension.begin(), extension.end(), extension.begin(),
|
||||||
|
[](unsigned char c) { return (char)std::tolower(c); });
|
||||||
|
|
||||||
|
if (extension != "tga" && extension != "exr" && extension != "png" &&
|
||||||
|
extension != "pfm" && extension != "hdr") {
|
||||||
|
std::string orig = fn;
|
||||||
|
fn.erase(offset + 1);
|
||||||
|
fn += "png";
|
||||||
|
|
||||||
|
// Does it already exist? Warn if not.
|
||||||
|
std::ifstream filestream(fn);
|
||||||
|
if (!filestream.good())
|
||||||
|
std::cerr << orig << ": must convert this texture to PNG.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PbrtExporter::RemoveSuffix(std::string filename) {
|
||||||
|
size_t offset = filename.rfind('.');
|
||||||
|
if (offset != std::string::npos)
|
||||||
|
filename.erase(offset);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteLights() {
|
||||||
|
mOutput << "\n";
|
||||||
|
mOutput << "#################\n";
|
||||||
|
mOutput << "# Lights\n\n";
|
||||||
|
if (mScene->mNumLights == 0) {
|
||||||
|
// Skip the default light if no cameras and this is flat up geometry
|
||||||
|
if (mScene->mNumCameras > 0) {
|
||||||
|
std::cerr << "No lights specified. Using default infinite light.\n";
|
||||||
|
|
||||||
|
mOutput << "AttributeBegin\n";
|
||||||
|
mOutput << " # default light\n";
|
||||||
|
mOutput << " LightSource \"infinite\" \"blackbody L\" [6000 1]\n";
|
||||||
|
|
||||||
|
mOutput << "AttributeEnd\n\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < mScene->mNumLights; ++i) {
|
||||||
|
const aiLight *light = mScene->mLights[i];
|
||||||
|
|
||||||
|
mOutput << "# Light " << light->mName.C_Str() << "\n";
|
||||||
|
mOutput << "AttributeBegin\n";
|
||||||
|
|
||||||
|
aiMatrix4x4 worldFromLight = GetNodeTransform(light->mName);
|
||||||
|
mOutput << " Transform [ " << TransformAsString(worldFromLight) << " ]\n";
|
||||||
|
|
||||||
|
aiColor3D color = light->mColorDiffuse + light->mColorSpecular;
|
||||||
|
if (light->mAttenuationConstant != 0)
|
||||||
|
color = color * (ai_real)(1. / light->mAttenuationConstant);
|
||||||
|
|
||||||
|
switch (light->mType) {
|
||||||
|
case aiLightSource_DIRECTIONAL: {
|
||||||
|
mOutput << " LightSource \"distant\"\n";
|
||||||
|
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
|
||||||
|
light->mPosition.y << " " << light->mPosition.z << " ]\n";
|
||||||
|
aiVector3D to = light->mPosition + light->mDirection;
|
||||||
|
mOutput << " \"point3 to\" [ " << to.x << " " << to.y << " " << to.z << " ]\n";
|
||||||
|
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
|
||||||
|
break;
|
||||||
|
} case aiLightSource_POINT:
|
||||||
|
mOutput << " LightSource \"distant\"\n";
|
||||||
|
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
|
||||||
|
light->mPosition.y << " " << light->mPosition.z << " ]\n";
|
||||||
|
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
|
||||||
|
break;
|
||||||
|
case aiLightSource_SPOT: {
|
||||||
|
mOutput << " LightSource \"spot\"\n";
|
||||||
|
mOutput << " \"point3 from\" [ " << light->mPosition.x << " " <<
|
||||||
|
light->mPosition.y << " " << light->mPosition.z << " ]\n";
|
||||||
|
aiVector3D to = light->mPosition + light->mDirection;
|
||||||
|
mOutput << " \"point3 to\" [ " << to.x << " " << to.y << " " << to.z << " ]\n";
|
||||||
|
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
|
||||||
|
mOutput << " \"float coneangle\" [ " << AI_RAD_TO_DEG(light->mAngleOuterCone) << " ]\n";
|
||||||
|
mOutput << " \"float conedeltaangle\" [ " << AI_RAD_TO_DEG(light->mAngleOuterCone -
|
||||||
|
light->mAngleInnerCone) << " ]\n";
|
||||||
|
break;
|
||||||
|
} case aiLightSource_AMBIENT:
|
||||||
|
mOutput << "# ignored ambient light source\n";
|
||||||
|
break;
|
||||||
|
case aiLightSource_AREA: {
|
||||||
|
aiVector3D left = light->mDirection ^ light->mUp;
|
||||||
|
// rectangle. center at position, direction is normal vector
|
||||||
|
float dLeft = light->mSize.x / 2, dUp = light->mSize.y / 2;
|
||||||
|
aiVector3D vertices[4] = {
|
||||||
|
light->mPosition - dLeft * left - dUp * light->mUp,
|
||||||
|
light->mPosition + dLeft * left - dUp * light->mUp,
|
||||||
|
light->mPosition - dLeft * left + dUp * light->mUp,
|
||||||
|
light->mPosition + dLeft * left + dUp * light->mUp };
|
||||||
|
mOutput << " AreaLightSource \"diffuse\"\n";
|
||||||
|
mOutput << " \"rgb L\" [ " << color.r << " " << color.g << " " << color.b << " ]\n";
|
||||||
|
mOutput << " Shape \"bilinearmesh\"\n";
|
||||||
|
mOutput << " \"point3 p\" [ ";
|
||||||
|
for (int j = 0; j < 4; ++j)
|
||||||
|
mOutput << vertices[j].x << " " << vertices[j].y << " " << vertices[j].z;
|
||||||
|
mOutput << " ]\n";
|
||||||
|
mOutput << " \"integer indices\" [ 0 1 2 3 ]\n";
|
||||||
|
break;
|
||||||
|
} default:
|
||||||
|
mOutput << "# ignored undefined light source type\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mOutput << "AttributeEnd\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteMesh(aiMesh* mesh) {
|
||||||
|
mOutput << "# - Mesh: ";
|
||||||
|
if (mesh->mName == aiString(""))
|
||||||
|
mOutput << "<No Name>\n";
|
||||||
|
else
|
||||||
|
mOutput << mesh->mName.C_Str() << "\n";
|
||||||
|
|
||||||
|
mOutput << "AttributeBegin\n";
|
||||||
|
aiMaterial* material = mScene->mMaterials[mesh->mMaterialIndex];
|
||||||
|
mOutput << " NamedMaterial \"" << material->GetName().C_Str() << "\"\n";
|
||||||
|
|
||||||
|
// Handle area lights
|
||||||
|
aiColor3D emission;
|
||||||
|
if (material->Get(AI_MATKEY_COLOR_EMISSIVE, emission) == AI_SUCCESS &&
|
||||||
|
(emission.r > 0 || emission.g > 0 || emission.b > 0))
|
||||||
|
mOutput << " AreaLightSource \"diffuse\" \"rgb L\" [ " << emission.r <<
|
||||||
|
" " << emission.g << " " << emission.b << " ]\n";
|
||||||
|
|
||||||
|
// Check if any types other than tri
|
||||||
|
if ( (mesh->mPrimitiveTypes & aiPrimitiveType_POINT)
|
||||||
|
|| (mesh->mPrimitiveTypes & aiPrimitiveType_LINE)
|
||||||
|
|| (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
|
||||||
|
std::cerr << "Error: ignoring point / line / polygon mesh " << mesh->mName.C_Str() << ".\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpha mask
|
||||||
|
std::string alpha;
|
||||||
|
aiString opacityTexture;
|
||||||
|
if (material->Get(AI_MATKEY_TEXTURE_OPACITY(0), opacityTexture) == AI_SUCCESS ||
|
||||||
|
material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), opacityTexture) == AI_SUCCESS) {
|
||||||
|
// material->Get(AI_MATKEY_TEXTURE_BASE_COLOR(0), opacityTexture) == AI_SUCCESS)
|
||||||
|
std::string texName = std::string("alpha:") + CleanTextureFilename(opacityTexture);
|
||||||
|
if (mTextureSet.find(texName) != mTextureSet.end())
|
||||||
|
alpha = std::string(" \"texture alpha\" \"") + texName + "\"\n";
|
||||||
|
} else {
|
||||||
|
float opacity = 1;
|
||||||
|
if (material->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS && opacity < 1)
|
||||||
|
alpha = std::string(" \"float alpha\" [ ") + std::to_string(opacity) + " ]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the shape specification
|
||||||
|
mOutput << "Shape \"trianglemesh\"\n" <<
|
||||||
|
alpha <<
|
||||||
|
" \"integer indices\" [";
|
||||||
|
|
||||||
|
// Start with faces (which hold indices)
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
||||||
|
auto face = mesh->mFaces[i];
|
||||||
|
if (face.mNumIndices != 3) throw DeadlyExportError("oh no not a tri!");
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < face.mNumIndices; j++) {
|
||||||
|
mOutput << face.mIndices[j] << " ";
|
||||||
|
}
|
||||||
|
if ((i % 7) == 6) mOutput << "\n ";
|
||||||
|
}
|
||||||
|
mOutput << "]\n";
|
||||||
|
|
||||||
|
// Then go to vertices
|
||||||
|
mOutput << " \"point3 P\" [";
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
|
||||||
|
auto vector = mesh->mVertices[i];
|
||||||
|
mOutput << vector.x << " " << vector.y << " " << vector.z << " ";
|
||||||
|
if ((i % 4) == 3) mOutput << "\n ";
|
||||||
|
}
|
||||||
|
mOutput << "]\n";
|
||||||
|
|
||||||
|
// Normals (if present)
|
||||||
|
if (mesh->mNormals) {
|
||||||
|
mOutput << " \"normal N\" [";
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
|
||||||
|
auto normal = mesh->mNormals[i];
|
||||||
|
mOutput << normal.x << " " << normal.y << " " << normal.z << " ";
|
||||||
|
if ((i % 4) == 3) mOutput << "\n ";
|
||||||
|
}
|
||||||
|
mOutput << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tangents (if present)
|
||||||
|
if (mesh->mTangents) {
|
||||||
|
mOutput << " \"vector3 S\" [";
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
|
||||||
|
auto tangent = mesh->mTangents[i];
|
||||||
|
mOutput << tangent.x << " " << tangent.y << " " << tangent.z << " ";
|
||||||
|
if ((i % 4) == 3) mOutput << "\n ";
|
||||||
|
}
|
||||||
|
mOutput << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texture Coords (if present)
|
||||||
|
// Find the first set of 2D texture coordinates..
|
||||||
|
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
|
||||||
|
if (mesh->mNumUVComponents[i] == 2) {
|
||||||
|
// assert(mesh->mTextureCoords[i] != nullptr);
|
||||||
|
aiVector3D* uv = mesh->mTextureCoords[i];
|
||||||
|
mOutput << " \"point2 uv\" [";
|
||||||
|
for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
|
||||||
|
mOutput << uv[j].x << " " << uv[j].y << " ";
|
||||||
|
if ((j % 6) == 5) mOutput << "\n ";
|
||||||
|
}
|
||||||
|
mOutput << "]\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: issue warning if there are additional UV sets?
|
||||||
|
|
||||||
|
mOutput << "AttributeEnd\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteInstanceDefinition(int i) {
|
||||||
|
aiMesh* mesh = mScene->mMeshes[i];
|
||||||
|
|
||||||
|
mOutput << "ObjectBegin \"";
|
||||||
|
if (mesh->mName == aiString(""))
|
||||||
|
mOutput << "mesh_" << i+1 << "\"\n";
|
||||||
|
else
|
||||||
|
mOutput << mesh->mName.C_Str() << "_" << i+1 << "\"\n";
|
||||||
|
|
||||||
|
WriteMesh(mesh);
|
||||||
|
|
||||||
|
mOutput << "ObjectEnd\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PbrtExporter::WriteGeometricObjects(aiNode* node, aiMatrix4x4 worldFromObject,
|
||||||
|
std::map<int, int> &meshUses) {
|
||||||
|
// Sometimes interior nodes have degenerate matrices??
|
||||||
|
if (node->mTransformation.Determinant() != 0)
|
||||||
|
worldFromObject = worldFromObject * node->mTransformation;
|
||||||
|
|
||||||
|
if (node->mNumMeshes > 0) {
|
||||||
|
mOutput << "AttributeBegin\n";
|
||||||
|
|
||||||
|
mOutput << " Transform [ " << TransformAsString(worldFromObject) << "]\n";
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
||||||
|
aiMesh* mesh = mScene->mMeshes[node->mMeshes[i]];
|
||||||
|
if (meshUses[node->mMeshes[i]] == 1) {
|
||||||
|
// If it's only used once in the scene, emit it directly as
|
||||||
|
// a triangle mesh.
|
||||||
|
mOutput << " # " << mesh->mName.C_Str();
|
||||||
|
WriteMesh(mesh);
|
||||||
|
} else {
|
||||||
|
// If it's used multiple times, there will be an object
|
||||||
|
// instance for it, so emit a reference to that.
|
||||||
|
mOutput << " ObjectInstance \"";
|
||||||
|
if (mesh->mName == aiString(""))
|
||||||
|
mOutput << "mesh_" << node->mMeshes[i] + 1 << "\"\n";
|
||||||
|
else
|
||||||
|
mOutput << mesh->mName.C_Str() << "_" << node->mMeshes[i] + 1 << "\"\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mOutput << "AttributeEnd\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse through children
|
||||||
|
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
||||||
|
WriteGeometricObjects(node->mChildren[i], worldFromObject, meshUses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
with or without modification, are permitted provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other
|
||||||
|
materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the assimp team, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior
|
||||||
|
written permission of the assimp team.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file PbrtExporter.h
|
||||||
|
* Declares the exporter class to write a scene to a pbrt file
|
||||||
|
*/
|
||||||
|
#ifndef AI_PBRTEXPORTER_H_INC
|
||||||
|
#define AI_PBRTEXPORTER_H_INC
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
|
||||||
|
#include <assimp/types.h>
|
||||||
|
#include <assimp/StreamWriter.h>
|
||||||
|
#include <assimp/Exceptional.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
struct aiScene;
|
||||||
|
struct aiNode;
|
||||||
|
struct aiMaterial;
|
||||||
|
struct aiMesh;
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
class IOSystem;
|
||||||
|
class IOStream;
|
||||||
|
class ExportProperties;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
/** Helper class to export a given scene to a Pbrt file. */
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
class PbrtExporter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor for a specific scene to export
|
||||||
|
PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem,
|
||||||
|
const std::string path, const std::string file);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~PbrtExporter();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// the scene to export
|
||||||
|
const aiScene* mScene;
|
||||||
|
|
||||||
|
/// Stringstream to write all output into
|
||||||
|
std::stringstream mOutput;
|
||||||
|
|
||||||
|
/// The IOSystem for output
|
||||||
|
IOSystem* mIOSystem;
|
||||||
|
|
||||||
|
/// Path of the directory where the scene will be exported
|
||||||
|
const std::string mPath;
|
||||||
|
|
||||||
|
/// Name of the file (without extension) where the scene will be exported
|
||||||
|
const std::string mFile;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A private set to keep track of which textures have been declared
|
||||||
|
std::set<std::string> mTextureSet;
|
||||||
|
|
||||||
|
aiMatrix4x4 GetNodeTransform(const aiString& name) const;
|
||||||
|
static std::string TransformAsString(const aiMatrix4x4& m);
|
||||||
|
|
||||||
|
static std::string RemoveSuffix(std::string filename);
|
||||||
|
std::string CleanTextureFilename(const aiString &f, bool rewriteExtension = true) const;
|
||||||
|
|
||||||
|
void WriteMetaData();
|
||||||
|
|
||||||
|
void WriteWorldDefinition();
|
||||||
|
|
||||||
|
void WriteCameras();
|
||||||
|
void WriteCamera(int i);
|
||||||
|
|
||||||
|
void WriteLights();
|
||||||
|
|
||||||
|
void WriteTextures();
|
||||||
|
static bool TextureHasAlphaMask(const std::string &filename);
|
||||||
|
|
||||||
|
void WriteMaterials();
|
||||||
|
void WriteMaterial(int i);
|
||||||
|
|
||||||
|
void WriteMesh(aiMesh* mesh);
|
||||||
|
|
||||||
|
void WriteInstanceDefinition(int i);
|
||||||
|
void WriteGeometricObjects(aiNode* node, aiMatrix4x4 parentTransform,
|
||||||
|
std::map<int, int> &meshUses);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Assimp
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_PBRT_EXPORTER
|
||||||
|
|
||||||
|
#endif // AI_PBRTEXPORTER_H_INC
|
File diff suppressed because it is too large
Load Diff
|
@ -198,11 +198,9 @@ endif(MINGW)
|
||||||
|
|
||||||
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
||||||
|
|
||||||
IF(WIN32)
|
INSTALL( TARGETS zlibstatic
|
||||||
INSTALL( TARGETS zlibstatic
|
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
|
||||||
LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
|
ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
|
||||||
ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
|
RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
|
||||||
RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
|
COMPONENT ${LIBASSIMP_COMPONENT})
|
||||||
COMPONENT ${LIBASSIMP_COMPONENT})
|
|
||||||
ENDIF()
|
|
||||||
|
|
|
@ -735,9 +735,9 @@ public:
|
||||||
* #GetTextureCount() can be used to determine the number of textures
|
* #GetTextureCount() can be used to determine the number of textures
|
||||||
* per texture type.
|
* per texture type.
|
||||||
* @param path Receives the path to the texture.
|
* @param path Receives the path to the texture.
|
||||||
* If the texture is embedded, receives a '*' followed by the id of
|
* Use aiScene::GetEmbeddedTexture() method to determine if returned path
|
||||||
* the texture (for the textures stored in the corresponding scene) which
|
* is an image file to be opened or a string key of embedded texture stored in the corresponding scene
|
||||||
* can be converted to an int using a function like atoi.
|
* (could be a '*' followed by the id of the texture in case of no name)
|
||||||
* NULL is a valid value.
|
* NULL is a valid value.
|
||||||
* @param mapping The texture mapping.
|
* @param mapping The texture mapping.
|
||||||
* NULL is allowed as value.
|
* NULL is allowed as value.
|
||||||
|
|
|
@ -305,9 +305,9 @@ struct aiString {
|
||||||
|
|
||||||
/** Copy a const char* to the aiString */
|
/** Copy a const char* to the aiString */
|
||||||
void Set(const char *sz) {
|
void Set(const char *sz) {
|
||||||
const ai_int32 len = (ai_uint32)::strlen(sz);
|
ai_int32 len = (ai_uint32)::strlen(sz);
|
||||||
if (len > (ai_int32)MAXLEN - 1) {
|
if (len > (ai_int32)MAXLEN - 1) {
|
||||||
return;
|
len = (ai_int32) MAXLEN - 1;
|
||||||
}
|
}
|
||||||
length = len;
|
length = len;
|
||||||
memcpy(data, sz, len);
|
memcpy(data, sz, len);
|
||||||
|
@ -321,7 +321,10 @@ struct aiString {
|
||||||
}
|
}
|
||||||
|
|
||||||
length = rOther.length;
|
length = rOther.length;
|
||||||
;
|
if (length >(MAXLEN - 1)) {
|
||||||
|
length = (ai_int32) MAXLEN - 1;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(data, rOther.data, length);
|
memcpy(data, rOther.data, length);
|
||||||
data[length] = '\0';
|
data[length] = '\0';
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2020, assimp team
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -154,6 +152,9 @@ const aiVector3t<TReal>& aiVector3t<TReal>::operator *= (TReal f) {
|
||||||
template <typename TReal>
|
template <typename TReal>
|
||||||
AI_FORCE_INLINE
|
AI_FORCE_INLINE
|
||||||
const aiVector3t<TReal>& aiVector3t<TReal>::operator /= (TReal f) {
|
const aiVector3t<TReal>& aiVector3t<TReal>::operator /= (TReal f) {
|
||||||
|
if ( f == static_cast<TReal>(0.0)) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
const TReal invF = (TReal) 1.0 / f;
|
const TReal invF = (TReal) 1.0 / f;
|
||||||
x *= invF;
|
x *= invF;
|
||||||
y *= invF;
|
y *= invF;
|
||||||
|
|
Loading…
Reference in New Issue