2015-05-19 03:48:29 +00:00
|
|
|
/*
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
Open Asset Import Library (assimp)
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
2021-02-28 11:17:54 +00:00
|
|
|
Copyright (c) 2006-2021, assimp team
|
2018-01-28 18:42:05 +00:00
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
All rights reserved.
|
|
|
|
|
2015-05-19 03:52:10 +00:00
|
|
|
Redistribution and use of this software in source and binary forms,
|
|
|
|
with or without modification, are permitted provided that the following
|
2015-05-19 03:48:29 +00:00
|
|
|
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.
|
|
|
|
|
2015-05-19 03:52:10 +00:00
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
2015-05-19 03:48:29 +00:00
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
2015-05-19 03:52:10 +00:00
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
2015-05-19 03:48:29 +00:00
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
2015-05-19 03:52:10 +00:00
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
2015-05-19 03:48:29 +00:00
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
2015-05-19 03:52:10 +00:00
|
|
|
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
|
2015-05-19 03:48:29 +00:00
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @file Implementation of the STL importer class */
|
|
|
|
|
|
|
|
#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
|
|
|
|
|
|
|
|
#include "STLLoader.h"
|
2018-01-06 00:18:33 +00:00
|
|
|
#include <assimp/ParsingUtils.h>
|
|
|
|
#include <assimp/fast_atof.h>
|
2020-03-08 20:24:01 +00:00
|
|
|
#include <assimp/importerdesc.h>
|
2016-06-06 20:04:29 +00:00
|
|
|
#include <assimp/scene.h>
|
|
|
|
#include <assimp/DefaultLogger.hpp>
|
2020-03-08 20:24:01 +00:00
|
|
|
#include <assimp/IOSystem.hpp>
|
|
|
|
#include <memory>
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
using namespace Assimp;
|
|
|
|
|
|
|
|
namespace {
|
2020-03-08 20:24:01 +00:00
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
static const aiImporterDesc desc = {
|
2015-05-19 03:57:13 +00:00
|
|
|
"Stereolithography (STL) Importer",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
"stl"
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// A valid binary STL buffer should consist of the following elements, in order:
|
|
|
|
// 1) 80 byte header
|
|
|
|
// 2) 4 byte face count
|
|
|
|
// 3) 50 bytes per face
|
2020-03-08 20:24:01 +00:00
|
|
|
static bool IsBinarySTL(const char *buffer, unsigned int fileSize) {
|
|
|
|
if (fileSize < 84) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return false;
|
2015-08-08 08:55:10 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2017-11-07 09:47:27 +00:00
|
|
|
const char *facecount_pos = buffer + 80;
|
2020-03-08 20:24:01 +00:00
|
|
|
uint32_t faceCount(0);
|
|
|
|
::memcpy(&faceCount, facecount_pos, sizeof(uint32_t));
|
2015-05-19 03:57:13 +00:00
|
|
|
const uint32_t expectedBinaryFileSize = faceCount * 50 + 84;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
return expectedBinaryFileSize == fileSize;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 13:11:24 +00:00
|
|
|
static const size_t BufferSize = 500;
|
|
|
|
static const char UnicodeBoundary = 127;
|
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
// An ascii STL buffer will begin with "solid NAME", where NAME is optional.
|
|
|
|
// Note: The "solid NAME" check is necessary, but not sufficient, to determine
|
|
|
|
// if the buffer is ASCII; a binary header could also begin with "solid NAME".
|
2020-03-08 20:24:01 +00:00
|
|
|
static bool IsAsciiSTL(const char *buffer, unsigned int fileSize) {
|
2015-05-19 03:57:13 +00:00
|
|
|
if (IsBinarySTL(buffer, fileSize))
|
|
|
|
return false;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
const char *bufferEnd = buffer + fileSize;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2020-06-09 08:06:50 +00:00
|
|
|
if (!SkipSpaces(&buffer)) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return false;
|
2020-06-09 08:06:50 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2020-06-09 08:06:50 +00:00
|
|
|
if (buffer + 5 >= bufferEnd) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return false;
|
2020-06-09 08:06:50 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
bool isASCII(strncmp(buffer, "solid", 5) == 0);
|
|
|
|
if (isASCII) {
|
2015-08-08 09:57:15 +00:00
|
|
|
// A lot of importers are write solid even if the file is binary. So we have to check for ASCII-characters.
|
2020-03-08 20:24:01 +00:00
|
|
|
if (fileSize >= BufferSize) {
|
2015-08-08 08:55:10 +00:00
|
|
|
isASCII = true;
|
2020-03-08 20:24:01 +00:00
|
|
|
for (unsigned int i = 0; i < BufferSize; i++) {
|
|
|
|
if (buffer[i] > UnicodeBoundary) {
|
2015-08-08 08:55:10 +00:00
|
|
|
isASCII = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return isASCII;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
// Constructor to be privately used by Importer
|
2020-03-08 20:24:01 +00:00
|
|
|
STLImporter::STLImporter() :
|
|
|
|
mBuffer(),
|
|
|
|
mFileSize(0),
|
|
|
|
mScene() {
|
|
|
|
// empty
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2015-05-19 03:52:10 +00:00
|
|
|
// Destructor, private as well
|
2020-03-08 20:24:01 +00:00
|
|
|
STLImporter::~STLImporter() {
|
|
|
|
// empty
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2015-05-19 03:52:10 +00:00
|
|
|
// Returns whether the class can handle the format of the given file.
|
2020-03-08 20:24:01 +00:00
|
|
|
bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
|
2015-05-19 03:57:13 +00:00
|
|
|
const std::string extension = GetExtension(pFile);
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (extension == "stl") {
|
2015-05-19 03:57:13 +00:00
|
|
|
return true;
|
2021-09-13 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!extension.length() || checkSig) {
|
2020-03-08 20:24:01 +00:00
|
|
|
if (!pIOHandler) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return true;
|
2015-08-08 08:55:10 +00:00
|
|
|
}
|
2021-09-11 21:23:05 +00:00
|
|
|
static const char * const tokens[] = { "STL", "solid" };
|
2020-03-08 20:24:01 +00:00
|
|
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2016-07-16 02:14:36 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
return false;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2020-03-08 20:24:01 +00:00
|
|
|
const aiImporterDesc *STLImporter::GetInfo() const {
|
2015-05-19 03:57:13 +00:00
|
|
|
return &desc;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
void addFacesToMesh(aiMesh *pMesh) {
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
|
2020-03-08 20:24:01 +00:00
|
|
|
for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces; ++i) {
|
2015-08-06 10:04:05 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
aiFace &face = pMesh->mFaces[i];
|
2015-08-06 10:04:05 +00:00
|
|
|
face.mIndices = new unsigned int[face.mNumIndices = 3];
|
2020-03-08 20:24:01 +00:00
|
|
|
for (unsigned int o = 0; o < 3; ++o, ++p) {
|
2015-08-06 10:04:05 +00:00
|
|
|
face.mIndices[o] = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
2015-05-19 03:52:10 +00:00
|
|
|
// Imports the given file into the given scene structure.
|
2020-03-08 20:24:01 +00:00
|
|
|
void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
|
|
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// Check whether we can read from the file
|
2020-03-08 20:24:01 +00:00
|
|
|
if (file.get() == nullptr) {
|
2020-08-18 16:35:08 +00:00
|
|
|
throw DeadlyImportError("Failed to open STL file ", pFile, ".");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
mFileSize = (unsigned int)file->FileSize();
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// allocate storage and copy the contents of the file to a memory buffer
|
|
|
|
// (terminate it with zero)
|
2018-12-30 15:04:49 +00:00
|
|
|
std::vector<char> buffer2;
|
2020-03-08 20:24:01 +00:00
|
|
|
TextFileToBuffer(file.get(), buffer2);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene = pScene;
|
|
|
|
mBuffer = &buffer2[0];
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// the default vertex color is light gray.
|
2020-03-08 20:24:01 +00:00
|
|
|
mClrColorDefault.r = mClrColorDefault.g = mClrColorDefault.b = mClrColorDefault.a = (ai_real)0.6;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// allocate a single node
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mRootNode = new aiNode();
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
bool bMatClr = false;
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (IsBinarySTL(mBuffer, mFileSize)) {
|
2015-05-19 03:57:13 +00:00
|
|
|
bMatClr = LoadBinaryFile();
|
2020-03-08 20:24:01 +00:00
|
|
|
} else if (IsAsciiSTL(mBuffer, mFileSize)) {
|
|
|
|
LoadASCIIFile(mScene->mRootNode);
|
2015-05-19 03:57:13 +00:00
|
|
|
} else {
|
2020-08-18 16:35:08 +00:00
|
|
|
throw DeadlyImportError("Failed to determine STL storage representation for ", pFile, ".");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 11:24:56 +00:00
|
|
|
// create a single default material, using a white diffuse color for consistency with
|
2015-05-19 03:57:13 +00:00
|
|
|
// other geometric types (e.g., PLY).
|
2020-03-08 20:24:01 +00:00
|
|
|
aiMaterial *pcMat = new aiMaterial();
|
2015-05-19 03:57:13 +00:00
|
|
|
aiString s;
|
|
|
|
s.Set(AI_DEFAULT_MATERIAL_NAME);
|
2018-09-10 13:41:12 +00:00
|
|
|
pcMat->AddProperty(&s, AI_MATKEY_NAME);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
aiColor4D clrDiffuse(ai_real(1.0), ai_real(1.0), ai_real(1.0), ai_real(1.0));
|
2015-05-19 03:57:13 +00:00
|
|
|
if (bMatClr) {
|
2020-03-08 20:24:01 +00:00
|
|
|
clrDiffuse = mClrColorDefault;
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
|
|
|
pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_SPECULAR);
|
|
|
|
clrDiffuse = aiColor4D(ai_real(0.05), ai_real(0.05), ai_real(0.05), ai_real(1.0));
|
|
|
|
pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_AMBIENT);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mNumMaterials = 1;
|
|
|
|
mScene->mMaterials = new aiMaterial *[1];
|
|
|
|
mScene->mMaterials[0] = pcMat;
|
2018-12-30 15:04:49 +00:00
|
|
|
|
|
|
|
mBuffer = nullptr;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
2017-11-06 21:30:07 +00:00
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
// Read an ASCII STL file
|
2020-03-08 20:24:01 +00:00
|
|
|
void STLImporter::LoadASCIIFile(aiNode *root) {
|
|
|
|
std::vector<aiMesh *> meshes;
|
|
|
|
std::vector<aiNode *> nodes;
|
|
|
|
const char *sz = mBuffer;
|
|
|
|
const char *bufferEnd = mBuffer + mFileSize;
|
2015-08-06 10:04:05 +00:00
|
|
|
std::vector<aiVector3D> positionBuffer;
|
|
|
|
std::vector<aiVector3D> normalBuffer;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// try to guess how many vertices we could have
|
|
|
|
// assume we'll need 160 bytes for each face
|
2020-03-08 20:24:01 +00:00
|
|
|
size_t sizeEstimate = std::max(1u, mFileSize / 160u) * 3;
|
2015-08-06 10:04:05 +00:00
|
|
|
positionBuffer.reserve(sizeEstimate);
|
|
|
|
normalBuffer.reserve(sizeEstimate);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2017-11-06 21:30:07 +00:00
|
|
|
while (IsAsciiSTL(sz, static_cast<unsigned int>(bufferEnd - sz))) {
|
|
|
|
std::vector<unsigned int> meshIndices;
|
2020-03-08 20:24:01 +00:00
|
|
|
aiMesh *pMesh = new aiMesh();
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mMaterialIndex = 0;
|
2020-03-08 20:24:01 +00:00
|
|
|
meshIndices.push_back((unsigned int)meshes.size());
|
2015-08-06 10:04:05 +00:00
|
|
|
meshes.push_back(pMesh);
|
2017-11-06 21:30:07 +00:00
|
|
|
aiNode *node = new aiNode;
|
|
|
|
node->mParent = root;
|
2020-03-08 20:24:01 +00:00
|
|
|
nodes.push_back(node);
|
2015-08-06 10:04:05 +00:00
|
|
|
SkipSpaces(&sz);
|
|
|
|
ai_assert(!IsLineEnd(sz));
|
|
|
|
|
|
|
|
sz += 5; // skip the "solid"
|
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
const char *szMe = sz;
|
2015-08-06 10:04:05 +00:00
|
|
|
while (!::IsSpaceOrNewLine(*sz)) {
|
|
|
|
sz++;
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
size_t temp = (size_t)(sz - szMe);
|
2015-08-06 10:04:05 +00:00
|
|
|
// setup the name of the node
|
2020-03-08 20:24:01 +00:00
|
|
|
if ( temp ) {
|
2015-08-06 10:04:05 +00:00
|
|
|
if (temp >= MAXLEN) {
|
2020-03-08 20:24:01 +00:00
|
|
|
throw DeadlyImportError("STL: Node name too long");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
std::string name(szMe, temp);
|
|
|
|
node->mName.Set(name.c_str());
|
|
|
|
pMesh->mName.Set(name.c_str());
|
2017-11-06 21:30:07 +00:00
|
|
|
} else {
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mRootNode->mName.Set("<STL_ASCII>");
|
2015-08-06 10:04:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-20 14:14:33 +00:00
|
|
|
unsigned int faceVertexCounter = 3;
|
2020-03-08 20:24:01 +00:00
|
|
|
for (;;) {
|
2015-08-06 10:04:05 +00:00
|
|
|
// go to the next token
|
2020-03-08 20:24:01 +00:00
|
|
|
if (!SkipSpacesAndLineEnd(&sz)) {
|
2015-08-06 10:04:05 +00:00
|
|
|
// seems we're finished although there was no end marker
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_WARN("STL: unexpected EOF. \'endsolid\' keyword was expected");
|
2015-08-06 10:04:05 +00:00
|
|
|
break;
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
// facet normal -0.13 -0.13 -0.98
|
2020-03-08 20:24:01 +00:00
|
|
|
if (!strncmp(sz, "facet", 5) && IsSpaceOrNewLine(*(sz + 5)) && *(sz + 5) != '\0') {
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2015-08-06 10:04:05 +00:00
|
|
|
if (faceVertexCounter != 3) {
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_WARN("STL: A new facet begins but the old is not yet complete");
|
2015-08-06 10:04:05 +00:00
|
|
|
}
|
|
|
|
faceVertexCounter = 0;
|
|
|
|
normalBuffer.push_back(aiVector3D());
|
2020-03-08 20:24:01 +00:00
|
|
|
aiVector3D *vn = &normalBuffer.back();
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2015-08-06 10:04:05 +00:00
|
|
|
sz += 6;
|
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
if (strncmp(sz, "normal", 6)) {
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_WARN("STL: a facet normal vector was expected but not found");
|
2017-11-06 21:30:07 +00:00
|
|
|
} else {
|
2015-08-11 12:53:16 +00:00
|
|
|
if (sz[6] == '\0') {
|
|
|
|
throw DeadlyImportError("STL: unexpected EOF while parsing facet");
|
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
sz += 7;
|
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->x);
|
2015-08-06 10:04:05 +00:00
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->y);
|
2015-08-06 10:04:05 +00:00
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->z);
|
2015-08-06 10:04:05 +00:00
|
|
|
normalBuffer.push_back(*vn);
|
|
|
|
normalBuffer.push_back(*vn);
|
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
} else if (!strncmp(sz, "vertex", 6) && ::IsSpaceOrNewLine(*(sz + 6))) { // vertex 1.50000 1.50000 0.00000
|
2015-08-06 10:04:05 +00:00
|
|
|
if (faceVertexCounter >= 3) {
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_ERROR("STL: a facet with more than 3 vertices has been found");
|
2015-08-06 10:04:05 +00:00
|
|
|
++sz;
|
2017-11-06 21:30:07 +00:00
|
|
|
} else {
|
2015-08-11 12:53:16 +00:00
|
|
|
if (sz[6] == '\0') {
|
|
|
|
throw DeadlyImportError("STL: unexpected EOF while parsing facet");
|
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
sz += 7;
|
|
|
|
SkipSpaces(&sz);
|
|
|
|
positionBuffer.push_back(aiVector3D());
|
2020-03-08 20:24:01 +00:00
|
|
|
aiVector3D *vn = &positionBuffer.back();
|
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->x);
|
2015-08-06 10:04:05 +00:00
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->y);
|
2015-08-06 10:04:05 +00:00
|
|
|
SkipSpaces(&sz);
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->z);
|
2015-08-06 10:04:05 +00:00
|
|
|
faceVertexCounter++;
|
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
} else if (!::strncmp(sz, "endsolid", 8)) {
|
2015-08-06 10:04:05 +00:00
|
|
|
do {
|
|
|
|
++sz;
|
|
|
|
} while (!::IsLineEnd(*sz));
|
|
|
|
SkipSpacesAndLineEnd(&sz);
|
|
|
|
// finished!
|
|
|
|
break;
|
2017-11-06 21:30:07 +00:00
|
|
|
} else { // else skip the whole identifier
|
2015-08-06 10:04:05 +00:00
|
|
|
do {
|
|
|
|
++sz;
|
|
|
|
} while (!::IsSpaceOrNewLine(*sz));
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (positionBuffer.empty()) {
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mNumFaces = 0;
|
2018-05-19 20:05:06 +00:00
|
|
|
ASSIMP_LOG_WARN("STL: mesh is empty or invalid; no data loaded");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
if (positionBuffer.size() % 3 != 0) {
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mNumFaces = 0;
|
|
|
|
throw DeadlyImportError("STL: Invalid number of vertices");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
if (normalBuffer.size() != positionBuffer.size()) {
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mNumFaces = 0;
|
|
|
|
throw DeadlyImportError("Normal buffer size does not match position buffer size");
|
|
|
|
}
|
2018-06-09 13:45:09 +00:00
|
|
|
|
|
|
|
// only process positionbuffer when filled, else exception when accessing with index operator
|
|
|
|
// see line 353: only warning is triggered
|
|
|
|
// see line 373(now): access to empty positionbuffer with index operator forced exception
|
|
|
|
if (!positionBuffer.empty()) {
|
|
|
|
pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3);
|
|
|
|
pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
|
|
|
|
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
2020-05-28 19:02:13 +00:00
|
|
|
for (size_t i=0; i<pMesh->mNumVertices; ++i ) {
|
|
|
|
pMesh->mVertices[i].x = positionBuffer[i].x;
|
2021-07-29 11:28:51 +00:00
|
|
|
pMesh->mVertices[i].y = positionBuffer[i].y;
|
2020-05-28 19:02:13 +00:00
|
|
|
pMesh->mVertices[i].z = positionBuffer[i].z;
|
|
|
|
}
|
2018-06-09 13:45:09 +00:00
|
|
|
positionBuffer.clear();
|
|
|
|
}
|
|
|
|
// also only process normalBuffer when filled, else exception when accessing with index operator
|
|
|
|
if (!normalBuffer.empty()) {
|
|
|
|
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
2020-05-28 19:02:13 +00:00
|
|
|
for (size_t i=0; i<pMesh->mNumVertices; ++i ) {
|
|
|
|
pMesh->mNormals[i].x = normalBuffer[i].x;
|
2021-07-29 11:28:51 +00:00
|
|
|
pMesh->mNormals[i].y = normalBuffer[i].y;
|
2020-05-28 19:02:13 +00:00
|
|
|
pMesh->mNormals[i].z = normalBuffer[i].z;
|
|
|
|
}
|
2018-06-09 13:45:09 +00:00
|
|
|
normalBuffer.clear();
|
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
|
|
|
|
// now copy faces
|
|
|
|
addFacesToMesh(pMesh);
|
2017-11-06 21:30:07 +00:00
|
|
|
|
|
|
|
// assign the meshes to the current node
|
2020-03-08 20:24:01 +00:00
|
|
|
pushMeshesToNode(meshIndices, node);
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2017-11-06 21:30:07 +00:00
|
|
|
|
2015-08-06 10:04:05 +00:00
|
|
|
// now add the loaded meshes
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mNumMeshes = (unsigned int)meshes.size();
|
|
|
|
mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
|
2017-11-06 21:30:07 +00:00
|
|
|
for (size_t i = 0; i < meshes.size(); i++) {
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mMeshes[i] = meshes[i];
|
2017-11-06 21:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
root->mNumChildren = (unsigned int)nodes.size();
|
|
|
|
root->mChildren = new aiNode *[root->mNumChildren];
|
|
|
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
|
|
root->mChildren[i] = nodes[i];
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
// Read a binary STL file
|
2020-03-08 20:24:01 +00:00
|
|
|
bool STLImporter::LoadBinaryFile() {
|
2015-08-06 10:04:05 +00:00
|
|
|
// allocate one mesh
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mNumMeshes = 1;
|
|
|
|
mScene->mMeshes = new aiMesh *[1];
|
|
|
|
aiMesh *pMesh = mScene->mMeshes[0] = new aiMesh();
|
2015-08-06 10:04:05 +00:00
|
|
|
pMesh->mMaterialIndex = 0;
|
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// skip the first 80 bytes
|
2020-03-08 20:24:01 +00:00
|
|
|
if (mFileSize < 84) {
|
2015-05-19 03:57:13 +00:00
|
|
|
throw DeadlyImportError("STL: file is too small for the header");
|
|
|
|
}
|
|
|
|
bool bIsMaterialise = false;
|
|
|
|
|
2016-04-03 00:38:00 +00:00
|
|
|
// search for an occurrence of "COLOR=" in the header
|
2020-03-08 20:24:01 +00:00
|
|
|
const unsigned char *sz2 = (const unsigned char *)mBuffer;
|
|
|
|
const unsigned char *const szEnd = sz2 + 80;
|
2015-05-19 03:57:13 +00:00
|
|
|
while (sz2 < szEnd) {
|
|
|
|
|
|
|
|
if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
|
2020-03-08 20:24:01 +00:00
|
|
|
'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) {
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// read the default vertex color for facets
|
|
|
|
bIsMaterialise = true;
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_INFO("STL: Taking code path for Materialise files");
|
2020-03-08 20:24:01 +00:00
|
|
|
const ai_real invByte = (ai_real)1.0 / (ai_real)255.0;
|
|
|
|
mClrColorDefault.r = (*sz2++) * invByte;
|
|
|
|
mClrColorDefault.g = (*sz2++) * invByte;
|
|
|
|
mClrColorDefault.b = (*sz2++) * invByte;
|
|
|
|
mClrColorDefault.a = (*sz2++) * invByte;
|
2015-05-19 03:57:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
const unsigned char *sz = (const unsigned char *)mBuffer + 80;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// now read the number of facets
|
2020-03-08 20:24:01 +00:00
|
|
|
mScene->mRootNode->mName.Set("<STL_BINARY>");
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
pMesh->mNumFaces = *((uint32_t *)sz);
|
2015-05-19 03:57:13 +00:00
|
|
|
sz += 4;
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (mFileSize < 84 + pMesh->mNumFaces * 50) {
|
2015-05-19 03:57:13 +00:00
|
|
|
throw DeadlyImportError("STL: file is too small to hold all facets");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pMesh->mNumFaces) {
|
|
|
|
throw DeadlyImportError("STL: file is empty. There are no facets defined");
|
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
pMesh->mNumVertices = pMesh->mNumFaces * 3;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2018-01-16 15:31:15 +00:00
|
|
|
aiVector3D *vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
|
|
|
aiVector3D *vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2018-03-06 18:48:11 +00:00
|
|
|
typedef aiVector3t<float> aiVector3F;
|
2020-03-08 20:24:01 +00:00
|
|
|
aiVector3F *theVec;
|
2018-03-07 22:26:01 +00:00
|
|
|
aiVector3F theVec3F;
|
2020-03-08 20:24:01 +00:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) {
|
2015-05-19 03:57:13 +00:00
|
|
|
// NOTE: Blender sometimes writes empty normals ... this is not
|
|
|
|
// our fault ... the RemoveInvalidData helper step should fix that
|
2018-03-06 18:48:11 +00:00
|
|
|
|
|
|
|
// There's one normal for the face in the STL; use it three times
|
|
|
|
// for vertex normals
|
2020-03-08 20:24:01 +00:00
|
|
|
theVec = (aiVector3F *)sz;
|
|
|
|
::memcpy(&theVec3F, theVec, sizeof(aiVector3F));
|
|
|
|
vn->x = theVec3F.x;
|
|
|
|
vn->y = theVec3F.y;
|
|
|
|
vn->z = theVec3F.z;
|
|
|
|
*(vn + 1) = *vn;
|
|
|
|
*(vn + 2) = *vn;
|
2018-03-07 22:26:01 +00:00
|
|
|
++theVec;
|
|
|
|
vn += 3;
|
2018-03-06 18:48:11 +00:00
|
|
|
|
|
|
|
// vertex 1
|
2020-03-08 20:24:01 +00:00
|
|
|
::memcpy(&theVec3F, theVec, sizeof(aiVector3F));
|
|
|
|
vp->x = theVec3F.x;
|
|
|
|
vp->y = theVec3F.y;
|
|
|
|
vp->z = theVec3F.z;
|
2018-03-07 22:26:01 +00:00
|
|
|
++theVec;
|
|
|
|
++vp;
|
2018-03-06 18:48:11 +00:00
|
|
|
|
|
|
|
// vertex 2
|
2020-03-08 20:24:01 +00:00
|
|
|
::memcpy(&theVec3F, theVec, sizeof(aiVector3F));
|
|
|
|
vp->x = theVec3F.x;
|
|
|
|
vp->y = theVec3F.y;
|
|
|
|
vp->z = theVec3F.z;
|
2018-03-07 22:26:01 +00:00
|
|
|
++theVec;
|
|
|
|
++vp;
|
2018-03-06 18:48:11 +00:00
|
|
|
|
|
|
|
// vertex 3
|
2020-03-08 20:24:01 +00:00
|
|
|
::memcpy(&theVec3F, theVec, sizeof(aiVector3F));
|
|
|
|
vp->x = theVec3F.x;
|
|
|
|
vp->y = theVec3F.y;
|
|
|
|
vp->z = theVec3F.z;
|
2018-03-07 22:26:01 +00:00
|
|
|
++theVec;
|
|
|
|
++vp;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
sz = (const unsigned char *)theVec;
|
|
|
|
|
|
|
|
uint16_t color = *((uint16_t *)sz);
|
2015-05-19 03:57:13 +00:00
|
|
|
sz += 2;
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (color & (1 << 15)) {
|
2015-05-19 03:57:13 +00:00
|
|
|
// seems we need to take the color
|
2020-03-08 20:24:01 +00:00
|
|
|
if (!pMesh->mColors[0]) {
|
2015-05-19 03:57:13 +00:00
|
|
|
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
|
2020-03-08 20:24:01 +00:00
|
|
|
for (unsigned int j = 0; j < pMesh->mNumVertices; ++j) {
|
|
|
|
*pMesh->mColors[0]++ = mClrColorDefault;
|
|
|
|
}
|
2015-05-19 03:57:13 +00:00
|
|
|
pMesh->mColors[0] -= pMesh->mNumVertices;
|
|
|
|
|
2018-04-26 12:10:18 +00:00
|
|
|
ASSIMP_LOG_INFO("STL: Mesh has vertex colors");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
aiColor4D *clr = &pMesh->mColors[0][i * 3];
|
2016-07-16 02:14:36 +00:00
|
|
|
clr->a = 1.0;
|
2020-03-08 20:24:01 +00:00
|
|
|
const ai_real invVal((ai_real)1.0 / (ai_real)31.0);
|
2015-05-19 03:57:13 +00:00
|
|
|
if (bIsMaterialise) // this is reversed
|
|
|
|
{
|
2020-03-08 20:24:01 +00:00
|
|
|
clr->r = (color & 0x31u) * invVal;
|
|
|
|
clr->g = ((color & (0x31u << 5)) >> 5u) * invVal;
|
|
|
|
clr->b = ((color & (0x31u << 10)) >> 10u) * invVal;
|
|
|
|
} else {
|
|
|
|
clr->b = (color & 0x31u) * invVal;
|
|
|
|
clr->g = ((color & (0x31u << 5)) >> 5u) * invVal;
|
|
|
|
clr->r = ((color & (0x31u << 10)) >> 10u) * invVal;
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
// assign the color to all vertices of the face
|
2020-03-08 20:24:01 +00:00
|
|
|
*(clr + 1) = *clr;
|
|
|
|
*(clr + 2) = *clr;
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-06 10:04:05 +00:00
|
|
|
|
|
|
|
// now copy faces
|
|
|
|
addFacesToMesh(pMesh);
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
aiNode *root = mScene->mRootNode;
|
2018-07-23 13:44:20 +00:00
|
|
|
|
|
|
|
// allocate one node
|
2020-03-08 20:24:01 +00:00
|
|
|
aiNode *node = new aiNode();
|
2018-07-23 13:44:20 +00:00
|
|
|
node->mParent = root;
|
|
|
|
|
|
|
|
root->mNumChildren = 1u;
|
2020-03-08 20:24:01 +00:00
|
|
|
root->mChildren = new aiNode *[root->mNumChildren];
|
2018-07-23 13:44:20 +00:00
|
|
|
root->mChildren[0] = node;
|
|
|
|
|
2017-11-29 16:20:09 +00:00
|
|
|
// add all created meshes to the single node
|
2020-03-08 20:24:01 +00:00
|
|
|
node->mNumMeshes = mScene->mNumMeshes;
|
|
|
|
node->mMeshes = new unsigned int[mScene->mNumMeshes];
|
|
|
|
for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
|
2018-07-23 13:44:20 +00:00
|
|
|
node->mMeshes[i] = i;
|
2020-03-08 20:24:01 +00:00
|
|
|
}
|
2017-11-29 16:20:09 +00:00
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (bIsMaterialise && !pMesh->mColors[0]) {
|
2015-05-19 03:57:13 +00:00
|
|
|
// use the color as diffuse material color
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
void STLImporter::pushMeshesToNode(std::vector<unsigned int> &meshIndices, aiNode *node) {
|
|
|
|
ai_assert(nullptr != node);
|
|
|
|
if (meshIndices.empty()) {
|
2017-11-06 21:30:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
node->mNumMeshes = static_cast<unsigned int>(meshIndices.size());
|
|
|
|
node->mMeshes = new unsigned int[meshIndices.size()];
|
|
|
|
for (size_t i = 0; i < meshIndices.size(); ++i) {
|
|
|
|
node->mMeshes[i] = meshIndices[i];
|
2017-11-06 21:30:07 +00:00
|
|
|
}
|
|
|
|
meshIndices.clear();
|
|
|
|
}
|
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
#endif // !! ASSIMP_BUILD_NO_STL_IMPORTER
|