Merge remote-tracking branch 'upstream/master'
commit
c6213836aa
|
@ -134,6 +134,12 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
|
||||||
OFF
|
OFF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IF ( WIN32 )
|
||||||
|
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
|
||||||
|
"If the Assimp view tool is built. (requires DirectX)"
|
||||||
|
OFF )
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
|
||||||
IF (NOT CMAKE_BUILD_TYPE)
|
IF (NOT CMAKE_BUILD_TYPE)
|
||||||
SET(CMAKE_BUILD_TYPE "Release")
|
SET(CMAKE_BUILD_TYPE "Release")
|
||||||
|
@ -512,19 +518,19 @@ ENDIF()
|
||||||
SET ( ASSIMP_BUILD_ARCHITECTURE "" CACHE STRING
|
SET ( ASSIMP_BUILD_ARCHITECTURE "" CACHE STRING
|
||||||
"describe the current architecture."
|
"describe the current architecture."
|
||||||
)
|
)
|
||||||
IF ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
|
IF( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
|
||||||
ELSE ()
|
ELSE()
|
||||||
ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
|
ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
|
||||||
ENDIF ()
|
ENDIF()
|
||||||
|
|
||||||
# ${CMAKE_GENERATOR}
|
# ${CMAKE_GENERATOR}
|
||||||
SET ( ASSIMP_BUILD_COMPILER "" CACHE STRING
|
SET ( ASSIMP_BUILD_COMPILER "" CACHE STRING
|
||||||
"describe the current compiler."
|
"describe the current compiler."
|
||||||
)
|
)
|
||||||
IF ( ASSIMP_BUILD_COMPILER STREQUAL "")
|
IF( ASSIMP_BUILD_COMPILER STREQUAL "")
|
||||||
ELSE ()
|
ELSE()
|
||||||
ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
|
ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
|
||||||
ENDIF ()
|
ENDIF()
|
||||||
|
|
||||||
MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
|
MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
|
||||||
|
|
||||||
|
|
|
@ -1,705 +0,0 @@
|
||||||
/*
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
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 AMFImporter.cpp
|
|
||||||
/// \brief AMF-format files importer for Assimp: main algorithm implementation.
|
|
||||||
/// \date 2016
|
|
||||||
/// \author smal.root@gmail.com
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
|
|
||||||
|
|
||||||
// Header files, Assimp.
|
|
||||||
#include "AMFImporter.hpp"
|
|
||||||
#include "AMFImporter_Macro.hpp"
|
|
||||||
|
|
||||||
#include <assimp/fast_atof.h>
|
|
||||||
#include <assimp/DefaultIOSystem.h>
|
|
||||||
|
|
||||||
// Header files, stdlib.
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace Assimp
|
|
||||||
{
|
|
||||||
|
|
||||||
/// \var aiImporterDesc AMFImporter::Description
|
|
||||||
/// Conastant which hold importer description
|
|
||||||
const aiImporterDesc AMFImporter::Description = {
|
|
||||||
"Additive manufacturing file format(AMF) Importer",
|
|
||||||
"smalcom",
|
|
||||||
"",
|
|
||||||
"See documentation in source code. Chapter: Limitations.",
|
|
||||||
aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
"amf"
|
|
||||||
};
|
|
||||||
|
|
||||||
void AMFImporter::Clear()
|
|
||||||
{
|
|
||||||
mNodeElement_Cur = nullptr;
|
|
||||||
mUnit.clear();
|
|
||||||
mMaterial_Converted.clear();
|
|
||||||
mTexture_Converted.clear();
|
|
||||||
// Delete all elements
|
|
||||||
if(!mNodeElement_List.empty())
|
|
||||||
{
|
|
||||||
for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
|
|
||||||
|
|
||||||
mNodeElement_List.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AMFImporter::~AMFImporter()
|
|
||||||
{
|
|
||||||
if(mReader != nullptr) delete mReader;
|
|
||||||
// Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
/************************************************************ Functions: find set ************************************************************/
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
|
|
||||||
bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
|
|
||||||
{
|
|
||||||
for(CAMFImporter_NodeElement* ne: mNodeElement_List)
|
|
||||||
{
|
|
||||||
if((ne->ID == pID) && (ne->Type == pType))
|
|
||||||
{
|
|
||||||
if(pNodeElement != nullptr) *pNodeElement = ne;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
|
|
||||||
{
|
|
||||||
aiString node_name(pID.c_str());
|
|
||||||
|
|
||||||
for(aiNode* node: pNodeList)
|
|
||||||
{
|
|
||||||
if(node->mName == node_name)
|
|
||||||
{
|
|
||||||
if(pNode != nullptr) *pNode = node;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}// for(aiNode* node: pNodeList)
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
|
|
||||||
{
|
|
||||||
for(const SPP_Material& mat: mMaterial_Converted)
|
|
||||||
{
|
|
||||||
if(mat.ID == pID)
|
|
||||||
{
|
|
||||||
if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}// for(const SPP_Material& mat: mMaterial_Converted)
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
/************************************************************ Functions: throw set ***********************************************************/
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
|
|
||||||
void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("Not found node with name \"" + pID + "\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
/************************************************************* Functions: XML set ************************************************************/
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
|
|
||||||
void AMFImporter::XML_CheckNode_MustHaveChildren()
|
|
||||||
{
|
|
||||||
if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
|
|
||||||
{
|
|
||||||
static const size_t Uns_Skip_Len = 3;
|
|
||||||
const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
|
|
||||||
|
|
||||||
static bool skipped_before[Uns_Skip_Len] = { false, false, false };
|
|
||||||
|
|
||||||
std::string nn(mReader->getNodeName());
|
|
||||||
bool found = false;
|
|
||||||
bool close_found = false;
|
|
||||||
size_t sk_idx;
|
|
||||||
|
|
||||||
for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
|
|
||||||
{
|
|
||||||
if(nn != Uns_Skip[sk_idx]) continue;
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
if(mReader->isEmptyElement())
|
|
||||||
{
|
|
||||||
close_found = true;
|
|
||||||
|
|
||||||
goto casu_cres;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(mReader->read())
|
|
||||||
{
|
|
||||||
if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
|
|
||||||
{
|
|
||||||
close_found = true;
|
|
||||||
|
|
||||||
goto casu_cres;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
|
|
||||||
|
|
||||||
casu_cres:
|
|
||||||
|
|
||||||
if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
|
|
||||||
if(!close_found) Throw_CloseNotFound(nn);
|
|
||||||
|
|
||||||
if(!skipped_before[sk_idx])
|
|
||||||
{
|
|
||||||
skipped_before[sk_idx] = true;
|
|
||||||
ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
|
|
||||||
{
|
|
||||||
while(mReader->read())
|
|
||||||
{
|
|
||||||
if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
|
|
||||||
{
|
|
||||||
std::string val(mReader->getAttributeValue(pAttrIdx));
|
|
||||||
|
|
||||||
if((val == "false") || (val == "0"))
|
|
||||||
return false;
|
|
||||||
else if((val == "true") || (val == "1"))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
|
|
||||||
{
|
|
||||||
std::string val;
|
|
||||||
float tvalf;
|
|
||||||
|
|
||||||
ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
|
|
||||||
fast_atoreal_move(val.c_str(), tvalf, false);
|
|
||||||
|
|
||||||
return tvalf;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
|
|
||||||
{
|
|
||||||
return strtoul10(mReader->getAttributeValue(pAttrIdx));
|
|
||||||
}
|
|
||||||
|
|
||||||
float AMFImporter::XML_ReadNode_GetVal_AsFloat()
|
|
||||||
{
|
|
||||||
std::string val;
|
|
||||||
float tvalf;
|
|
||||||
|
|
||||||
if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
|
|
||||||
if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
|
|
||||||
|
|
||||||
ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
|
|
||||||
fast_atoreal_move(val.c_str(), tvalf, false);
|
|
||||||
|
|
||||||
return tvalf;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
|
|
||||||
{
|
|
||||||
if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
|
|
||||||
if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
|
|
||||||
|
|
||||||
return strtoul10(mReader->getNodeData());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
|
|
||||||
{
|
|
||||||
if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
|
|
||||||
if(mReader->getNodeType() != irr::io::EXN_TEXT)
|
|
||||||
throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
|
|
||||||
|
|
||||||
pValue = mReader->getNodeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
/************************************************************ Functions: parse set ***********************************************************/
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
|
|
||||||
void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
|
|
||||||
{
|
|
||||||
mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
|
|
||||||
mNodeElement_Cur = pNode;// switch current element to new one.
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::ParseHelper_Node_Exit()
|
|
||||||
{
|
|
||||||
// check if we can walk up.
|
|
||||||
if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
|
|
||||||
{
|
|
||||||
size_t instr_len;
|
|
||||||
|
|
||||||
pOutString.clear();
|
|
||||||
instr_len = strlen(pInStr);
|
|
||||||
if(!instr_len) return;
|
|
||||||
|
|
||||||
pOutString.reserve(instr_len * 3 / 2);
|
|
||||||
// check and correct floats in format ".x". Must be "x.y".
|
|
||||||
if(pInStr[0] == '.') pOutString.push_back('0');
|
|
||||||
|
|
||||||
pOutString.push_back(pInStr[0]);
|
|
||||||
for(size_t ci = 1; ci < instr_len; ci++)
|
|
||||||
{
|
|
||||||
if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
|
|
||||||
{
|
|
||||||
pOutString.push_back('0');
|
|
||||||
pOutString.push_back('.');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pOutString.push_back(pInStr[ci]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
|
|
||||||
{
|
|
||||||
return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
|
|
||||||
{
|
|
||||||
// With help from
|
|
||||||
// René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
|
|
||||||
const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
|
|
||||||
uint8_t tidx = 0;
|
|
||||||
uint8_t arr4[4], arr3[3];
|
|
||||||
|
|
||||||
// check input data
|
|
||||||
if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
|
|
||||||
// prepare output place
|
|
||||||
pOutputData.clear();
|
|
||||||
pOutputData.reserve(pInputBase64.size() / 4 * 3);
|
|
||||||
|
|
||||||
for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
|
|
||||||
{
|
|
||||||
if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
|
|
||||||
{
|
|
||||||
arr4[tidx++] = pInputBase64[in_idx++];
|
|
||||||
if(tidx == 4)
|
|
||||||
{
|
|
||||||
for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
|
|
||||||
|
|
||||||
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
|
|
||||||
arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
|
|
||||||
arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
|
|
||||||
for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
|
|
||||||
|
|
||||||
tidx = 0;
|
|
||||||
}// if(tidx == 4)
|
|
||||||
}// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
|
|
||||||
else
|
|
||||||
{
|
|
||||||
in_idx++;
|
|
||||||
}// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tidx)
|
|
||||||
{
|
|
||||||
for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
|
|
||||||
for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
|
|
||||||
|
|
||||||
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
|
|
||||||
arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
|
|
||||||
arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
|
|
||||||
for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
|
|
||||||
{
|
|
||||||
irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
|
|
||||||
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
|
||||||
|
|
||||||
// Check whether we can read from the file
|
|
||||||
if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
|
|
||||||
|
|
||||||
// generate a XML reader for it
|
|
||||||
std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
|
|
||||||
mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
|
|
||||||
if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
|
|
||||||
//
|
|
||||||
// start reading
|
|
||||||
// search for root tag <amf>
|
|
||||||
if(XML_SearchNode("amf"))
|
|
||||||
ParseNode_Root();
|
|
||||||
else
|
|
||||||
throw DeadlyImportError("Root node \"amf\" not found.");
|
|
||||||
|
|
||||||
delete mReader;
|
|
||||||
// restore old XMLreader
|
|
||||||
mReader = OldReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// <amf
|
|
||||||
// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
|
|
||||||
// version="" - Version of file format.
|
|
||||||
// >
|
|
||||||
// </amf>
|
|
||||||
// Root XML element.
|
|
||||||
// Multi elements - No.
|
|
||||||
void AMFImporter::ParseNode_Root()
|
|
||||||
{
|
|
||||||
std::string unit, version;
|
|
||||||
CAMFImporter_NodeElement *ne( nullptr );
|
|
||||||
|
|
||||||
// Read attributes for node <amf>.
|
|
||||||
MACRO_ATTRREAD_LOOPBEG;
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_LOOPEND_WSKIP;
|
|
||||||
|
|
||||||
// Check attributes
|
|
||||||
if(!mUnit.empty())
|
|
||||||
{
|
|
||||||
if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create root node element.
|
|
||||||
ne = new CAMFImporter_NodeElement_Root(nullptr);
|
|
||||||
mNodeElement_Cur = ne;// set first "current" element
|
|
||||||
// and assign attribute's values
|
|
||||||
((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
|
|
||||||
((CAMFImporter_NodeElement_Root*)ne)->Version = version;
|
|
||||||
|
|
||||||
// Check for child nodes
|
|
||||||
if(!mReader->isEmptyElement())
|
|
||||||
{
|
|
||||||
MACRO_NODECHECK_LOOPBEGIN("amf");
|
|
||||||
if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
|
|
||||||
MACRO_NODECHECK_LOOPEND("amf");
|
|
||||||
mNodeElement_Cur = ne;// force restore "current" element
|
|
||||||
}// if(!mReader->isEmptyElement())
|
|
||||||
|
|
||||||
mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
|
|
||||||
}
|
|
||||||
|
|
||||||
// <constellation
|
|
||||||
// id="" - The Object ID of the new constellation being defined.
|
|
||||||
// >
|
|
||||||
// </constellation>
|
|
||||||
// A collection of objects or constellations with specific relative locations.
|
|
||||||
// Multi elements - Yes.
|
|
||||||
// Parent element - <amf>.
|
|
||||||
void AMFImporter::ParseNode_Constellation()
|
|
||||||
{
|
|
||||||
std::string id;
|
|
||||||
CAMFImporter_NodeElement* ne( nullptr );
|
|
||||||
|
|
||||||
// Read attributes for node <constellation>.
|
|
||||||
MACRO_ATTRREAD_LOOPBEG;
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_LOOPEND;
|
|
||||||
|
|
||||||
// create and if needed - define new grouping object.
|
|
||||||
ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
|
|
||||||
|
|
||||||
CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
|
|
||||||
|
|
||||||
if(!id.empty()) als.ID = id;
|
|
||||||
// Check for child nodes
|
|
||||||
if(!mReader->isEmptyElement())
|
|
||||||
{
|
|
||||||
ParseHelper_Node_Enter(ne);
|
|
||||||
MACRO_NODECHECK_LOOPBEGIN("constellation");
|
|
||||||
if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
|
|
||||||
MACRO_NODECHECK_LOOPEND("constellation");
|
|
||||||
ParseHelper_Node_Exit();
|
|
||||||
}// if(!mReader->isEmptyElement())
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
|
|
||||||
}// if(!mReader->isEmptyElement()) else
|
|
||||||
|
|
||||||
mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
|
|
||||||
}
|
|
||||||
|
|
||||||
// <instance
|
|
||||||
// objectid="" - The Object ID of the new constellation being defined.
|
|
||||||
// >
|
|
||||||
// </instance>
|
|
||||||
// A collection of objects or constellations with specific relative locations.
|
|
||||||
// Multi elements - Yes.
|
|
||||||
// Parent element - <amf>.
|
|
||||||
void AMFImporter::ParseNode_Instance()
|
|
||||||
{
|
|
||||||
std::string objectid;
|
|
||||||
CAMFImporter_NodeElement* ne( nullptr );
|
|
||||||
|
|
||||||
// Read attributes for node <constellation>.
|
|
||||||
MACRO_ATTRREAD_LOOPBEG;
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_LOOPEND;
|
|
||||||
|
|
||||||
// used object id must be defined, check that.
|
|
||||||
if(objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
|
|
||||||
// create and define new grouping object.
|
|
||||||
ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
|
|
||||||
|
|
||||||
CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
|
|
||||||
|
|
||||||
als.ObjectID = objectid;
|
|
||||||
// Check for child nodes
|
|
||||||
if(!mReader->isEmptyElement())
|
|
||||||
{
|
|
||||||
bool read_flag[6] = { false, false, false, false, false, false };
|
|
||||||
|
|
||||||
als.Delta.Set(0, 0, 0);
|
|
||||||
als.Rotation.Set(0, 0, 0);
|
|
||||||
ParseHelper_Node_Enter(ne);
|
|
||||||
MACRO_NODECHECK_LOOPBEGIN("instance");
|
|
||||||
MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
|
|
||||||
MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
|
|
||||||
MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
|
|
||||||
MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
|
|
||||||
MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
|
|
||||||
MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
|
|
||||||
MACRO_NODECHECK_LOOPEND("instance");
|
|
||||||
ParseHelper_Node_Exit();
|
|
||||||
// also convert degrees to radians.
|
|
||||||
als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
|
|
||||||
als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
|
|
||||||
als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
|
|
||||||
}// if(!mReader->isEmptyElement())
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
|
|
||||||
}// if(!mReader->isEmptyElement()) else
|
|
||||||
|
|
||||||
mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
|
|
||||||
}
|
|
||||||
|
|
||||||
// <object
|
|
||||||
// id="" - A unique ObjectID for the new object being defined.
|
|
||||||
// >
|
|
||||||
// </object>
|
|
||||||
// An object definition.
|
|
||||||
// Multi elements - Yes.
|
|
||||||
// Parent element - <amf>.
|
|
||||||
void AMFImporter::ParseNode_Object()
|
|
||||||
{
|
|
||||||
std::string id;
|
|
||||||
CAMFImporter_NodeElement* ne( nullptr );
|
|
||||||
|
|
||||||
// Read attributes for node <object>.
|
|
||||||
MACRO_ATTRREAD_LOOPBEG;
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_LOOPEND;
|
|
||||||
|
|
||||||
// create and if needed - define new geometry object.
|
|
||||||
ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
|
|
||||||
|
|
||||||
CAMFImporter_NodeElement_Object& als = *((CAMFImporter_NodeElement_Object*)ne);// alias for convenience
|
|
||||||
|
|
||||||
if(!id.empty()) als.ID = id;
|
|
||||||
// Check for child nodes
|
|
||||||
if(!mReader->isEmptyElement())
|
|
||||||
{
|
|
||||||
bool col_read = false;
|
|
||||||
|
|
||||||
ParseHelper_Node_Enter(ne);
|
|
||||||
MACRO_NODECHECK_LOOPBEGIN("object");
|
|
||||||
if(XML_CheckNode_NameEqual("color"))
|
|
||||||
{
|
|
||||||
// Check if color already defined for object.
|
|
||||||
if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
|
|
||||||
// read data and set flag about it
|
|
||||||
ParseNode_Color();
|
|
||||||
col_read = true;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(XML_CheckNode_NameEqual("mesh")) { ParseNode_Mesh(); continue; }
|
|
||||||
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
|
|
||||||
MACRO_NODECHECK_LOOPEND("object");
|
|
||||||
ParseHelper_Node_Exit();
|
|
||||||
}// if(!mReader->isEmptyElement())
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
|
|
||||||
}// if(!mReader->isEmptyElement()) else
|
|
||||||
|
|
||||||
mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
|
|
||||||
}
|
|
||||||
|
|
||||||
// <metadata
|
|
||||||
// type="" - The type of the attribute.
|
|
||||||
// >
|
|
||||||
// </metadata>
|
|
||||||
// Specify additional information about an entity.
|
|
||||||
// Multi elements - Yes.
|
|
||||||
// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
|
|
||||||
//
|
|
||||||
// Reserved types are:
|
|
||||||
// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
|
|
||||||
// "Description" - A description of the content of the entity
|
|
||||||
// "URL" - A link to an external resource relating to the entity
|
|
||||||
// "Author" - Specifies the name(s) of the author(s) of the entity
|
|
||||||
// "Company" - Specifying the company generating the entity
|
|
||||||
// "CAD" - specifies the name of the originating CAD software and version
|
|
||||||
// "Revision" - specifies the revision of the entity
|
|
||||||
// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
|
|
||||||
// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
|
|
||||||
void AMFImporter::ParseNode_Metadata()
|
|
||||||
{
|
|
||||||
std::string type, value;
|
|
||||||
CAMFImporter_NodeElement* ne( nullptr );
|
|
||||||
|
|
||||||
// read attribute
|
|
||||||
MACRO_ATTRREAD_LOOPBEG;
|
|
||||||
MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
|
|
||||||
MACRO_ATTRREAD_LOOPEND;
|
|
||||||
// and value of node.
|
|
||||||
value = mReader->getNodeData();
|
|
||||||
// Create node element and assign read data.
|
|
||||||
ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
|
|
||||||
((CAMFImporter_NodeElement_Metadata*)ne)->Type = type;
|
|
||||||
((CAMFImporter_NodeElement_Metadata*)ne)->Value = value;
|
|
||||||
mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
|
|
||||||
mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
/******************************************************** Functions: BaseImporter set ********************************************************/
|
|
||||||
/*********************************************************************************************************************************************/
|
|
||||||
|
|
||||||
bool AMFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
|
|
||||||
{
|
|
||||||
const std::string extension = GetExtension(pFile);
|
|
||||||
|
|
||||||
if ( extension == "amf" ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!extension.length() || pCheckSig)
|
|
||||||
{
|
|
||||||
const char* tokens[] = { "<amf" };
|
|
||||||
|
|
||||||
return SearchFileHeaderForToken( pIOHandler, pFile, tokens, 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::GetExtensionList(std::set<std::string>& pExtensionList)
|
|
||||||
{
|
|
||||||
pExtensionList.insert("amf");
|
|
||||||
}
|
|
||||||
|
|
||||||
const aiImporterDesc* AMFImporter::GetInfo () const
|
|
||||||
{
|
|
||||||
return &Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
|
||||||
{
|
|
||||||
Clear();// delete old graph.
|
|
||||||
ParseFile(pFile, pIOHandler);
|
|
||||||
Postprocess_BuildScene(pScene);
|
|
||||||
// scene graph is ready, exit.
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Assimp
|
|
||||||
|
|
||||||
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER
|
|
|
@ -1,978 +0,0 @@
|
||||||
/*
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
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 AMFImporter_Postprocess.cpp
|
|
||||||
/// \brief Convert built scenegraph and objects to Assimp scenegraph.
|
|
||||||
/// \date 2016
|
|
||||||
/// \author smal.root@gmail.com
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
|
|
||||||
|
|
||||||
#include "AMFImporter.hpp"
|
|
||||||
|
|
||||||
// Header files, Assimp.
|
|
||||||
#include <assimp/SceneCombiner.h>
|
|
||||||
#include <assimp/StandardShapes.h>
|
|
||||||
#include <assimp/StringUtils.h>
|
|
||||||
|
|
||||||
// Header files, stdlib.
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
namespace Assimp
|
|
||||||
{
|
|
||||||
|
|
||||||
aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const
|
|
||||||
{
|
|
||||||
aiColor4D tcol;
|
|
||||||
|
|
||||||
// Check if stored data are supported.
|
|
||||||
if(!Composition.empty())
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("IME. GetColor for composition");
|
|
||||||
}
|
|
||||||
else if(Color->Composed)
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("IME. GetColor, composed color");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tcol = Color->Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if default color must be used
|
|
||||||
if((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0))
|
|
||||||
{
|
|
||||||
tcol.r = 0.5f;
|
|
||||||
tcol.g = 0.5f;
|
|
||||||
tcol.b = 0.5f;
|
|
||||||
tcol.a = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tcol;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
|
|
||||||
std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const
|
|
||||||
{
|
|
||||||
CAMFImporter_NodeElement_Vertices* vn = nullptr;
|
|
||||||
size_t col_idx;
|
|
||||||
|
|
||||||
// All data stored in "vertices", search for it.
|
|
||||||
for(CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
|
|
||||||
{
|
|
||||||
if(ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices*)ne_child;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If "vertices" not found then no work for us.
|
|
||||||
if(vn == nullptr) return;
|
|
||||||
|
|
||||||
pVertexCoordinateArray.reserve(vn->Child.size());// all coordinates stored as child and we need to reserve space for future push_back's.
|
|
||||||
pVertexColorArray.resize(vn->Child.size());// colors count equal vertices count.
|
|
||||||
col_idx = 0;
|
|
||||||
// Inside vertices collect all data and place to arrays
|
|
||||||
for(CAMFImporter_NodeElement* vn_child: vn->Child)
|
|
||||||
{
|
|
||||||
// vertices, colors
|
|
||||||
if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
|
|
||||||
{
|
|
||||||
// by default clear color for current vertex
|
|
||||||
pVertexColorArray[col_idx] = nullptr;
|
|
||||||
|
|
||||||
for(CAMFImporter_NodeElement* vtx: vn_child->Child)
|
|
||||||
{
|
|
||||||
if(vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates)
|
|
||||||
{
|
|
||||||
pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates*)vtx)->Coordinate);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vtx->Type == CAMFImporter_NodeElement::ENET_Color)
|
|
||||||
{
|
|
||||||
pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color*)vtx;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}// for(CAMFImporter_NodeElement* vtx: vn_child->Child)
|
|
||||||
|
|
||||||
col_idx++;
|
|
||||||
}// if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
|
|
||||||
}// for(CAMFImporter_NodeElement* vn_child: vn->Child)
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B,
|
|
||||||
const std::string& pID_A)
|
|
||||||
{
|
|
||||||
size_t TextureConverted_Index;
|
|
||||||
std::string TextureConverted_ID;
|
|
||||||
|
|
||||||
// check input data
|
|
||||||
if(pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
|
|
||||||
throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
|
|
||||||
|
|
||||||
// Create ID
|
|
||||||
TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
|
|
||||||
// Check if texture specified by set of IDs is converted already.
|
|
||||||
TextureConverted_Index = 0;
|
|
||||||
for(const SPP_Texture& tex_convd: mTexture_Converted)
|
|
||||||
{
|
|
||||||
if ( tex_convd.ID == TextureConverted_ID ) {
|
|
||||||
return TextureConverted_Index;
|
|
||||||
} else {
|
|
||||||
++TextureConverted_Index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Converted texture not found, create it.
|
|
||||||
//
|
|
||||||
CAMFImporter_NodeElement_Texture* src_texture[4]{nullptr};
|
|
||||||
std::vector<CAMFImporter_NodeElement_Texture*> src_texture_4check;
|
|
||||||
SPP_Texture converted_texture;
|
|
||||||
|
|
||||||
{// find all specified source textures
|
|
||||||
CAMFImporter_NodeElement* t_tex;
|
|
||||||
|
|
||||||
// R
|
|
||||||
if(!pID_R.empty())
|
|
||||||
{
|
|
||||||
if(!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
|
|
||||||
|
|
||||||
src_texture[0] = (CAMFImporter_NodeElement_Texture*)t_tex;
|
|
||||||
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src_texture[0] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// G
|
|
||||||
if(!pID_G.empty())
|
|
||||||
{
|
|
||||||
if(!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
|
|
||||||
|
|
||||||
src_texture[1] = (CAMFImporter_NodeElement_Texture*)t_tex;
|
|
||||||
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src_texture[1] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// B
|
|
||||||
if(!pID_B.empty())
|
|
||||||
{
|
|
||||||
if(!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
|
|
||||||
|
|
||||||
src_texture[2] = (CAMFImporter_NodeElement_Texture*)t_tex;
|
|
||||||
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src_texture[2] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A
|
|
||||||
if(!pID_A.empty())
|
|
||||||
{
|
|
||||||
if(!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
|
|
||||||
|
|
||||||
src_texture[3] = (CAMFImporter_NodeElement_Texture*)t_tex;
|
|
||||||
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src_texture[3] = nullptr;
|
|
||||||
}
|
|
||||||
}// END: find all specified source textures
|
|
||||||
|
|
||||||
// check that all textures has same size
|
|
||||||
if(src_texture_4check.size() > 1)
|
|
||||||
{
|
|
||||||
for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++)
|
|
||||||
{
|
|
||||||
if((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
|
|
||||||
(src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth))
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}// if(src_texture_4check.size() > 1)
|
|
||||||
|
|
||||||
// set texture attributes
|
|
||||||
converted_texture.Width = src_texture_4check[0]->Width;
|
|
||||||
converted_texture.Height = src_texture_4check[0]->Height;
|
|
||||||
converted_texture.Depth = src_texture_4check[0]->Depth;
|
|
||||||
// if one of source texture is tiled then converted texture is tiled too.
|
|
||||||
converted_texture.Tiled = false;
|
|
||||||
for(uint8_t i = 0; i < src_texture_4check.size(); i++) converted_texture.Tiled |= src_texture_4check[i]->Tiled;
|
|
||||||
|
|
||||||
// Create format hint.
|
|
||||||
strcpy(converted_texture.FormatHint, "rgba0000");// copy initial string.
|
|
||||||
if(!pID_R.empty()) converted_texture.FormatHint[4] = '8';
|
|
||||||
if(!pID_G.empty()) converted_texture.FormatHint[5] = '8';
|
|
||||||
if(!pID_B.empty()) converted_texture.FormatHint[6] = '8';
|
|
||||||
if(!pID_A.empty()) converted_texture.FormatHint[7] = '8';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Сopy data of textures.
|
|
||||||
//
|
|
||||||
size_t tex_size = 0;
|
|
||||||
size_t step = 0;
|
|
||||||
size_t off_g = 0;
|
|
||||||
size_t off_b = 0;
|
|
||||||
|
|
||||||
// Calculate size of the target array and rule how data will be copied.
|
|
||||||
if(!pID_R.empty() && nullptr != src_texture[ 0 ] ) {
|
|
||||||
tex_size += src_texture[0]->Data.size(); step++, off_g++, off_b++;
|
|
||||||
}
|
|
||||||
if(!pID_G.empty() && nullptr != src_texture[ 1 ] ) {
|
|
||||||
tex_size += src_texture[1]->Data.size(); step++, off_b++;
|
|
||||||
}
|
|
||||||
if(!pID_B.empty() && nullptr != src_texture[ 2 ] ) {
|
|
||||||
tex_size += src_texture[2]->Data.size(); step++;
|
|
||||||
}
|
|
||||||
if(!pID_A.empty() && nullptr != src_texture[ 3 ] ) {
|
|
||||||
tex_size += src_texture[3]->Data.size(); step++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create target array.
|
|
||||||
converted_texture.Data = new uint8_t[tex_size];
|
|
||||||
// And copy data
|
|
||||||
auto CopyTextureData = [&](const std::string& pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
|
|
||||||
{
|
|
||||||
if(!pID.empty())
|
|
||||||
{
|
|
||||||
for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
|
|
||||||
CAMFImporter_NodeElement_Texture* tex = src_texture[pSrcTexNum];
|
|
||||||
ai_assert(tex);
|
|
||||||
converted_texture.Data[idx_target] = tex->Data.at(idx_src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
|
|
||||||
|
|
||||||
CopyTextureData(pID_R, 0, step, 0);
|
|
||||||
CopyTextureData(pID_G, off_g, step, 1);
|
|
||||||
CopyTextureData(pID_B, off_b, step, 2);
|
|
||||||
CopyTextureData(pID_A, step - 1, step, 3);
|
|
||||||
|
|
||||||
// Store new converted texture ID
|
|
||||||
converted_texture.ID = TextureConverted_ID;
|
|
||||||
// Store new converted texture
|
|
||||||
mTexture_Converted.push_back(converted_texture);
|
|
||||||
|
|
||||||
return TextureConverted_Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated)
|
|
||||||
{
|
|
||||||
auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap* pTexMap1, const CAMFImporter_NodeElement_TexMap* pTexMap2) -> bool
|
|
||||||
{
|
|
||||||
if((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
|
|
||||||
if(pTexMap1 == nullptr) return false;
|
|
||||||
if(pTexMap2 == nullptr) return false;
|
|
||||||
|
|
||||||
if(pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
|
|
||||||
if(pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
|
|
||||||
if(pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
|
|
||||||
if(pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
pOutputList_Separated.clear();
|
|
||||||
if(pInputList.empty()) return;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
SComplexFace face_start = pInputList.front();
|
|
||||||
std::list<SComplexFace> face_list_cur;
|
|
||||||
|
|
||||||
for(std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;)
|
|
||||||
{
|
|
||||||
if(texmap_is_equal(face_start.TexMap, it->TexMap))
|
|
||||||
{
|
|
||||||
auto it_old = it;
|
|
||||||
|
|
||||||
++it;
|
|
||||||
face_list_cur.push_back(*it_old);
|
|
||||||
pInputList.erase(it_old);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
|
|
||||||
|
|
||||||
} while(!pInputList.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& metadataList, aiNode& sceneNode) const
|
|
||||||
{
|
|
||||||
if ( !metadataList.empty() )
|
|
||||||
{
|
|
||||||
if(sceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
|
|
||||||
|
|
||||||
// copy collected metadata to output node.
|
|
||||||
sceneNode.mMetaData = aiMetadata::Alloc( static_cast<unsigned int>(metadataList.size()) );
|
|
||||||
size_t meta_idx( 0 );
|
|
||||||
|
|
||||||
for(const CAMFImporter_NodeElement_Metadata& metadata: metadataList)
|
|
||||||
{
|
|
||||||
sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
|
|
||||||
}
|
|
||||||
}// if(!metadataList.empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode)
|
|
||||||
{
|
|
||||||
CAMFImporter_NodeElement_Color* object_color = nullptr;
|
|
||||||
|
|
||||||
// create new aiNode and set name as <object> has.
|
|
||||||
*pSceneNode = new aiNode;
|
|
||||||
(*pSceneNode)->mName = pNodeElement.ID;
|
|
||||||
// read mesh and color
|
|
||||||
for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
|
|
||||||
{
|
|
||||||
std::vector<aiVector3D> vertex_arr;
|
|
||||||
std::vector<CAMFImporter_NodeElement_Color*> color_arr;
|
|
||||||
|
|
||||||
// color for object
|
|
||||||
if(ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color*)ne_child;
|
|
||||||
|
|
||||||
if(ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh)
|
|
||||||
{
|
|
||||||
// Create arrays from children of mesh: vertices.
|
|
||||||
PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr);
|
|
||||||
// Use this arrays as a source when creating every aiMesh
|
|
||||||
Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
|
|
||||||
}
|
|
||||||
}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh& pNodeElement, const std::vector<aiVector3D>& pVertexCoordinateArray,
|
|
||||||
const std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray,
|
|
||||||
const CAMFImporter_NodeElement_Color* pObjectColor, std::list<aiMesh*>& pMeshList, aiNode& pSceneNode)
|
|
||||||
{
|
|
||||||
std::list<unsigned int> mesh_idx;
|
|
||||||
|
|
||||||
// all data stored in "volume", search for it.
|
|
||||||
for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
|
|
||||||
{
|
|
||||||
const CAMFImporter_NodeElement_Color* ne_volume_color = nullptr;
|
|
||||||
const SPP_Material* cur_mat = nullptr;
|
|
||||||
|
|
||||||
if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
|
|
||||||
{
|
|
||||||
/******************* Get faces *******************/
|
|
||||||
const CAMFImporter_NodeElement_Volume* ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume*>(ne_child);
|
|
||||||
|
|
||||||
std::list<SComplexFace> complex_faces_list;// List of the faces of the volume.
|
|
||||||
std::list<std::list<SComplexFace> > complex_faces_toplist;// List of the face list for every mesh.
|
|
||||||
|
|
||||||
// check if volume use material
|
|
||||||
if(!ne_volume->MaterialID.empty())
|
|
||||||
{
|
|
||||||
if(!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// inside "volume" collect all data and place to arrays or create new objects
|
|
||||||
for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
|
|
||||||
{
|
|
||||||
// color for volume
|
|
||||||
if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color)
|
|
||||||
{
|
|
||||||
ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_volume_child);
|
|
||||||
}
|
|
||||||
else if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle)// triangles, triangles colors
|
|
||||||
{
|
|
||||||
const CAMFImporter_NodeElement_Triangle& tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle*>(ne_volume_child);
|
|
||||||
|
|
||||||
SComplexFace complex_face;
|
|
||||||
|
|
||||||
// initialize pointers
|
|
||||||
complex_face.Color = nullptr;
|
|
||||||
complex_face.TexMap = nullptr;
|
|
||||||
// get data from triangle children: color, texture coordinates.
|
|
||||||
if(tri_al.Child.size())
|
|
||||||
{
|
|
||||||
for(const CAMFImporter_NodeElement* ne_triangle_child: tri_al.Child)
|
|
||||||
{
|
|
||||||
if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
|
|
||||||
complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_triangle_child);
|
|
||||||
else if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
|
|
||||||
complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap*>(ne_triangle_child);
|
|
||||||
}
|
|
||||||
}// if(tri_al.Child.size())
|
|
||||||
|
|
||||||
// create new face and store it.
|
|
||||||
complex_face.Face.mNumIndices = 3;
|
|
||||||
complex_face.Face.mIndices = new unsigned int[3];
|
|
||||||
complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]);
|
|
||||||
complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]);
|
|
||||||
complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]);
|
|
||||||
complex_faces_list.push_back(complex_face);
|
|
||||||
}
|
|
||||||
}// for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
|
|
||||||
|
|
||||||
/**** Split faces list: one list per mesh ****/
|
|
||||||
PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
|
|
||||||
|
|
||||||
/***** Create mesh for every faces list ******/
|
|
||||||
for(std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
|
|
||||||
{
|
|
||||||
auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
|
|
||||||
{
|
|
||||||
size_t rv=0;
|
|
||||||
|
|
||||||
if(pBiggerThan != nullptr)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
for(const SComplexFace& face: pFaceList)
|
|
||||||
{
|
|
||||||
for(size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++)
|
|
||||||
{
|
|
||||||
if(face.Face.mIndices[idx_vert] > *pBiggerThan)
|
|
||||||
{
|
|
||||||
rv = face.Face.mIndices[idx_vert];
|
|
||||||
found = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(found) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!found) return *pBiggerThan;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rv = pFaceList.front().Face.mIndices[0];
|
|
||||||
}// if(pBiggerThan != nullptr) else
|
|
||||||
|
|
||||||
for(const SComplexFace& face: pFaceList)
|
|
||||||
{
|
|
||||||
for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
|
|
||||||
{
|
|
||||||
if(face.Face.mIndices[vi] < rv)
|
|
||||||
{
|
|
||||||
if(pBiggerThan != nullptr)
|
|
||||||
{
|
|
||||||
if(face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rv = face.Face.mIndices[vi];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}// for(const SComplexFace& face: pFaceList)
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
};// auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
|
|
||||||
|
|
||||||
auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
|
|
||||||
{
|
|
||||||
for(const SComplexFace& face: pFaceList)
|
|
||||||
{
|
|
||||||
for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
|
|
||||||
{
|
|
||||||
if(face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};// auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
|
|
||||||
|
|
||||||
auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
|
|
||||||
{
|
|
||||||
// Color priorities(In descending order):
|
|
||||||
// 1. triangle color;
|
|
||||||
// 2. vertex color;
|
|
||||||
// 3. volume color;
|
|
||||||
// 4. object color;
|
|
||||||
// 5. material;
|
|
||||||
// 6. default - invisible coat.
|
|
||||||
//
|
|
||||||
// Fill vertices colors in color priority list above that's points from 1 to 6.
|
|
||||||
if((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr))// check for vertex color
|
|
||||||
{
|
|
||||||
if(pVertexColorArray[pIdx]->Composed)
|
|
||||||
throw DeadlyImportError("IME: vertex color composed");
|
|
||||||
else
|
|
||||||
return pVertexColorArray[pIdx]->Color;
|
|
||||||
}
|
|
||||||
else if(ne_volume_color != nullptr)// check for volume color
|
|
||||||
{
|
|
||||||
if(ne_volume_color->Composed)
|
|
||||||
throw DeadlyImportError("IME: volume color composed");
|
|
||||||
else
|
|
||||||
return ne_volume_color->Color;
|
|
||||||
}
|
|
||||||
else if(pObjectColor != nullptr)// check for object color
|
|
||||||
{
|
|
||||||
if(pObjectColor->Composed)
|
|
||||||
throw DeadlyImportError("IME: object color composed");
|
|
||||||
else
|
|
||||||
return pObjectColor->Color;
|
|
||||||
}
|
|
||||||
else if(cur_mat != nullptr)// check for material
|
|
||||||
{
|
|
||||||
return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
|
|
||||||
}
|
|
||||||
else// set default color.
|
|
||||||
{
|
|
||||||
return {0, 0, 0, 0};
|
|
||||||
}// if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
|
|
||||||
|
|
||||||
};// auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
|
|
||||||
|
|
||||||
aiMesh* tmesh = new aiMesh;
|
|
||||||
|
|
||||||
tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;// Only triangles is supported by AMF.
|
|
||||||
//
|
|
||||||
// set geometry and colors (vertices)
|
|
||||||
//
|
|
||||||
// copy faces/triangles
|
|
||||||
tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size());
|
|
||||||
tmesh->mFaces = new aiFace[tmesh->mNumFaces];
|
|
||||||
|
|
||||||
// Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
|
|
||||||
// can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
|
|
||||||
// Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
|
|
||||||
size_t VertexCount_Max = tmesh->mNumFaces * 3;// 3 - triangles.
|
|
||||||
std::vector<aiVector3D> vert_arr, texcoord_arr;
|
|
||||||
std::vector<aiColor4D> col_arr;
|
|
||||||
|
|
||||||
vert_arr.reserve(VertexCount_Max * 2);// "* 2" - see below TODO.
|
|
||||||
col_arr.reserve(VertexCount_Max * 2);
|
|
||||||
|
|
||||||
{// fill arrays
|
|
||||||
size_t vert_idx_from, vert_idx_to;
|
|
||||||
|
|
||||||
// first iteration.
|
|
||||||
vert_idx_to = 0;
|
|
||||||
vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
|
|
||||||
vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
|
|
||||||
col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
|
|
||||||
if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
|
|
||||||
|
|
||||||
// rest iterations
|
|
||||||
do
|
|
||||||
{
|
|
||||||
vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
|
|
||||||
if(vert_idx_from == vert_idx_to) break;// all indices are transferred,
|
|
||||||
|
|
||||||
vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
|
|
||||||
col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
|
|
||||||
vert_idx_to++;
|
|
||||||
if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
|
|
||||||
|
|
||||||
} while(true);
|
|
||||||
}// fill arrays. END.
|
|
||||||
|
|
||||||
//
|
|
||||||
// check if triangle colors are used and create additional faces if needed.
|
|
||||||
//
|
|
||||||
for(const SComplexFace& face_cur: face_list_cur)
|
|
||||||
{
|
|
||||||
if(face_cur.Color != nullptr)
|
|
||||||
{
|
|
||||||
aiColor4D face_color;
|
|
||||||
size_t vert_idx_new = vert_arr.size();
|
|
||||||
|
|
||||||
if(face_cur.Color->Composed)
|
|
||||||
throw DeadlyImportError("IME: face color composed");
|
|
||||||
else
|
|
||||||
face_color = face_cur.Color->Color;
|
|
||||||
|
|
||||||
for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
|
|
||||||
{
|
|
||||||
vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
|
|
||||||
col_arr.push_back(face_color);
|
|
||||||
face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++);
|
|
||||||
}
|
|
||||||
}// if(face_cur.Color != nullptr)
|
|
||||||
}// for(const SComplexFace& face_cur: face_list_cur)
|
|
||||||
|
|
||||||
//
|
|
||||||
// if texture is used then copy texture coordinates too.
|
|
||||||
//
|
|
||||||
if(face_list_cur.front().TexMap != nullptr)
|
|
||||||
{
|
|
||||||
size_t idx_vert_new = vert_arr.size();
|
|
||||||
///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
|
|
||||||
/// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
|
|
||||||
/// optimisation.
|
|
||||||
bool* idx_vert_used;
|
|
||||||
|
|
||||||
idx_vert_used = new bool[VertexCount_Max * 2];
|
|
||||||
for(size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++) idx_vert_used[i] = false;
|
|
||||||
|
|
||||||
// This ID's will be used when set materials ID in scene.
|
|
||||||
tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
|
|
||||||
face_list_cur.front().TexMap->TextureID_G,
|
|
||||||
face_list_cur.front().TexMap->TextureID_B,
|
|
||||||
face_list_cur.front().TexMap->TextureID_A));
|
|
||||||
texcoord_arr.resize(VertexCount_Max * 2);
|
|
||||||
for(const SComplexFace& face_cur: face_list_cur)
|
|
||||||
{
|
|
||||||
for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
|
|
||||||
{
|
|
||||||
const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
|
|
||||||
|
|
||||||
if(!idx_vert_used[idx_vert])
|
|
||||||
{
|
|
||||||
texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
|
|
||||||
idx_vert_used[idx_vert] = true;
|
|
||||||
}
|
|
||||||
else if(texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind])
|
|
||||||
{
|
|
||||||
// in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
|
|
||||||
// coordinates.
|
|
||||||
vert_arr.push_back(vert_arr.at(idx_vert));
|
|
||||||
col_arr.push_back(col_arr.at(idx_vert));
|
|
||||||
texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
|
|
||||||
face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++);
|
|
||||||
}
|
|
||||||
}// for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
|
|
||||||
}// for(const SComplexFace& face_cur: face_list_cur)
|
|
||||||
|
|
||||||
delete [] idx_vert_used;
|
|
||||||
// shrink array
|
|
||||||
texcoord_arr.resize(idx_vert_new);
|
|
||||||
}// if(face_list_cur.front().TexMap != nullptr)
|
|
||||||
|
|
||||||
//
|
|
||||||
// copy collected data to mesh
|
|
||||||
//
|
|
||||||
tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
|
|
||||||
tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
|
|
||||||
tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
|
|
||||||
|
|
||||||
memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
|
|
||||||
memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
|
|
||||||
if(texcoord_arr.size() > 0)
|
|
||||||
{
|
|
||||||
tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
|
|
||||||
memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
|
|
||||||
tmesh->mNumUVComponents[0] = 2;// U and V stored in "x", "y" of aiVector3D.
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t idx_face = 0;
|
|
||||||
for(const SComplexFace& face_cur: face_list_cur) tmesh->mFaces[idx_face++] = face_cur.Face;
|
|
||||||
|
|
||||||
// store new aiMesh
|
|
||||||
mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size()));
|
|
||||||
pMeshList.push_back(tmesh);
|
|
||||||
}// for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
|
|
||||||
}// if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
|
|
||||||
}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
|
|
||||||
|
|
||||||
// if meshes was created then assign new indices with current aiNode
|
|
||||||
if(!mesh_idx.empty())
|
|
||||||
{
|
|
||||||
std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
|
|
||||||
|
|
||||||
pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size());
|
|
||||||
pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
|
|
||||||
for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *mit++;
|
|
||||||
}// if(mesh_idx.size() > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial)
|
|
||||||
{
|
|
||||||
SPP_Material new_mat;
|
|
||||||
|
|
||||||
new_mat.ID = pMaterial.ID;
|
|
||||||
for(const CAMFImporter_NodeElement* mat_child: pMaterial.Child)
|
|
||||||
{
|
|
||||||
if(mat_child->Type == CAMFImporter_NodeElement::ENET_Color)
|
|
||||||
{
|
|
||||||
new_mat.Color = (CAMFImporter_NodeElement_Color*)mat_child;
|
|
||||||
}
|
|
||||||
else if(mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata)
|
|
||||||
{
|
|
||||||
new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata*)mat_child);
|
|
||||||
}
|
|
||||||
}// for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
|
|
||||||
|
|
||||||
// place converted material to special list
|
|
||||||
mMaterial_Converted.push_back(new_mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const
|
|
||||||
{
|
|
||||||
aiNode* con_node;
|
|
||||||
std::list<aiNode*> ch_node;
|
|
||||||
|
|
||||||
// We will build next hierarchy:
|
|
||||||
// aiNode as parent (<constellation>) for set of nodes as a children
|
|
||||||
// |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
|
|
||||||
// ...
|
|
||||||
// \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
|
|
||||||
con_node = new aiNode;
|
|
||||||
con_node->mName = pConstellation.ID;
|
|
||||||
// Walk through children and search for instances of another objects, constellations.
|
|
||||||
for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
|
|
||||||
{
|
|
||||||
aiMatrix4x4 tmat;
|
|
||||||
aiNode* t_node;
|
|
||||||
aiNode* found_node;
|
|
||||||
|
|
||||||
if(ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
|
|
||||||
if(ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
|
|
||||||
|
|
||||||
// create alias for conveniance
|
|
||||||
CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);
|
|
||||||
// find referenced object
|
|
||||||
if(!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
|
|
||||||
|
|
||||||
// create node for applying transformation
|
|
||||||
t_node = new aiNode;
|
|
||||||
t_node->mParent = con_node;
|
|
||||||
// apply transformation
|
|
||||||
aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
|
|
||||||
aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
|
|
||||||
aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
|
|
||||||
aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
|
|
||||||
// create array for one child node
|
|
||||||
t_node->mNumChildren = 1;
|
|
||||||
t_node->mChildren = new aiNode*[t_node->mNumChildren];
|
|
||||||
SceneCombiner::Copy(&t_node->mChildren[0], found_node);
|
|
||||||
t_node->mChildren[0]->mParent = t_node;
|
|
||||||
ch_node.push_back(t_node);
|
|
||||||
}// for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
|
|
||||||
|
|
||||||
// copy found aiNode's as children
|
|
||||||
if(ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
|
|
||||||
|
|
||||||
size_t ch_idx = 0;
|
|
||||||
|
|
||||||
con_node->mNumChildren = static_cast<unsigned int>(ch_node.size());
|
|
||||||
con_node->mChildren = new aiNode*[con_node->mNumChildren];
|
|
||||||
for(aiNode* node: ch_node) con_node->mChildren[ch_idx++] = node;
|
|
||||||
|
|
||||||
// and place "root" of <constellation> node to node list
|
|
||||||
pNodeList.push_back(con_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMFImporter::Postprocess_BuildScene(aiScene* pScene)
|
|
||||||
{
|
|
||||||
std::list<aiNode*> node_list;
|
|
||||||
std::list<aiMesh*> mesh_list;
|
|
||||||
std::list<CAMFImporter_NodeElement_Metadata*> meta_list;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
|
|
||||||
// For building aiScene we are must to do few steps:
|
|
||||||
// at first creating root node for aiScene.
|
|
||||||
pScene->mRootNode = new aiNode;
|
|
||||||
pScene->mRootNode->mParent = nullptr;
|
|
||||||
pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
|
|
||||||
// search for root(<amf>) element
|
|
||||||
CAMFImporter_NodeElement* root_el = nullptr;
|
|
||||||
|
|
||||||
for(CAMFImporter_NodeElement* ne: mNodeElement_List)
|
|
||||||
{
|
|
||||||
if(ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
|
|
||||||
|
|
||||||
root_el = ne;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}// for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
|
|
||||||
|
|
||||||
// Check if root element are found.
|
|
||||||
if(root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
|
|
||||||
|
|
||||||
// after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
|
|
||||||
// <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
|
|
||||||
// at any moment.
|
|
||||||
//
|
|
||||||
// 1. <material>
|
|
||||||
// 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
|
|
||||||
for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
|
||||||
{
|
|
||||||
if(root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material*)root_child));
|
|
||||||
}
|
|
||||||
|
|
||||||
// After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
|
|
||||||
//
|
|
||||||
// 3. <object>
|
|
||||||
for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
|
||||||
{
|
|
||||||
if(root_child->Type == CAMFImporter_NodeElement::ENET_Object)
|
|
||||||
{
|
|
||||||
aiNode* tnode = nullptr;
|
|
||||||
|
|
||||||
// for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
|
|
||||||
Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object*)root_child), mesh_list, &tnode);
|
|
||||||
if(tnode != nullptr) node_list.push_back(tnode);
|
|
||||||
|
|
||||||
}
|
|
||||||
}// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
|
||||||
|
|
||||||
// And finally read rest of nodes.
|
|
||||||
//
|
|
||||||
for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
|
||||||
{
|
|
||||||
// 4. <constellation>
|
|
||||||
if(root_child->Type == CAMFImporter_NodeElement::ENET_Constellation)
|
|
||||||
{
|
|
||||||
// <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
|
|
||||||
Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation*)root_child), node_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5, <metadata>
|
|
||||||
if(root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata*)root_child);
|
|
||||||
}// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
|
||||||
|
|
||||||
// at now we can add collected metadata to root node
|
|
||||||
Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
|
|
||||||
//
|
|
||||||
// Check constellation children
|
|
||||||
//
|
|
||||||
// As said in specification:
|
|
||||||
// "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
|
|
||||||
// What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
|
|
||||||
// And at this step we are checking that relations.
|
|
||||||
nl_clean_loop:
|
|
||||||
|
|
||||||
if(node_list.size() > 1)
|
|
||||||
{
|
|
||||||
// walk through all nodes
|
|
||||||
for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it)
|
|
||||||
{
|
|
||||||
// and try to find them in another top nodes.
|
|
||||||
std::list<aiNode*>::const_iterator next_it = nl_it;
|
|
||||||
|
|
||||||
++next_it;
|
|
||||||
for(; next_it != node_list.end(); ++next_it)
|
|
||||||
{
|
|
||||||
if((*next_it)->FindNode((*nl_it)->mName) != nullptr)
|
|
||||||
{
|
|
||||||
// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
|
|
||||||
node_list.erase(nl_it);
|
|
||||||
|
|
||||||
goto nl_clean_loop;
|
|
||||||
}
|
|
||||||
}// for(; next_it != node_list.end(); next_it++)
|
|
||||||
}// for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// move created objects to aiScene
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Nodes
|
|
||||||
if(!node_list.empty())
|
|
||||||
{
|
|
||||||
std::list<aiNode*>::const_iterator nl_it = node_list.begin();
|
|
||||||
|
|
||||||
pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
|
|
||||||
pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
|
|
||||||
for(size_t i = 0; i < pScene->mRootNode->mNumChildren; i++)
|
|
||||||
{
|
|
||||||
// Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
|
|
||||||
// mRootNode only as parent.
|
|
||||||
(*nl_it)->mParent = pScene->mRootNode;
|
|
||||||
pScene->mRootNode->mChildren[i] = *nl_it++;
|
|
||||||
}
|
|
||||||
}// if(node_list.size() > 0)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Meshes
|
|
||||||
if(!mesh_list.empty())
|
|
||||||
{
|
|
||||||
std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
|
|
||||||
|
|
||||||
pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
|
|
||||||
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
|
|
||||||
for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *ml_it++;
|
|
||||||
}// if(mesh_list.size() > 0)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Textures
|
|
||||||
pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size());
|
|
||||||
if(pScene->mNumTextures > 0)
|
|
||||||
{
|
|
||||||
size_t idx;
|
|
||||||
|
|
||||||
idx = 0;
|
|
||||||
pScene->mTextures = new aiTexture*[pScene->mNumTextures];
|
|
||||||
for(const SPP_Texture& tex_convd: mTexture_Converted)
|
|
||||||
{
|
|
||||||
pScene->mTextures[idx] = new aiTexture;
|
|
||||||
pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width);
|
|
||||||
pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
|
|
||||||
pScene->mTextures[idx]->pcData = (aiTexel*)tex_convd.Data;
|
|
||||||
// texture format description.
|
|
||||||
strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
|
|
||||||
idx++;
|
|
||||||
}// for(const SPP_Texture& tex_convd: mTexture_Converted)
|
|
||||||
|
|
||||||
// Create materials for embedded textures.
|
|
||||||
idx = 0;
|
|
||||||
pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size());
|
|
||||||
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
|
|
||||||
for(const SPP_Texture& tex_convd: mTexture_Converted)
|
|
||||||
{
|
|
||||||
const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + to_string(idx));
|
|
||||||
const int mode = aiTextureOp_Multiply;
|
|
||||||
const int repeat = tex_convd.Tiled ? 1 : 0;
|
|
||||||
|
|
||||||
pScene->mMaterials[idx] = new aiMaterial;
|
|
||||||
pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
|
||||||
pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
|
|
||||||
pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
|
|
||||||
pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}// if(pScene->mNumTextures > 0)
|
|
||||||
}// END: after that walk through children of root and collect data
|
|
||||||
|
|
||||||
}// namespace Assimp
|
|
||||||
|
|
||||||
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER
|
|
|
@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// internal headers
|
// internal headers
|
||||||
#include "3DSLoader.h"
|
#include "3DSLoader.h"
|
||||||
#include "Common/TargetAnimation.h"
|
#include "Common/TargetAnimation.h"
|
||||||
|
#include <assimp/StringComparison.h>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/StringComparison.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
@ -58,75 +58,66 @@ static const unsigned int NotSet = 0xcdcdcdcd;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Setup final material indices, generae a default material if necessary
|
// Setup final material indices, generae a default material if necessary
|
||||||
void Discreet3DSImporter::ReplaceDefaultMaterial()
|
void Discreet3DSImporter::ReplaceDefaultMaterial() {
|
||||||
{
|
|
||||||
// Try to find an existing material that matches the
|
// Try to find an existing material that matches the
|
||||||
// typical default material setting:
|
// typical default material setting:
|
||||||
// - no textures
|
// - no textures
|
||||||
// - diffuse color (in grey!)
|
// - diffuse color (in grey!)
|
||||||
// NOTE: This is here to workaround the fact that some
|
// NOTE: This is here to workaround the fact that some
|
||||||
// exporters are writing a default material, too.
|
// exporters are writing a default material, too.
|
||||||
unsigned int idx( NotSet );
|
unsigned int idx(NotSet);
|
||||||
for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
|
for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
|
||||||
{
|
|
||||||
std::string s = mScene->mMaterials[i].mName;
|
std::string s = mScene->mMaterials[i].mName;
|
||||||
for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
|
for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
|
||||||
*it = static_cast< char >( ::tolower( *it ) );
|
*it = static_cast<char>(::tolower(*it));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::string::npos == s.find("default"))continue;
|
if (std::string::npos == s.find("default")) continue;
|
||||||
|
|
||||||
if (mScene->mMaterials[i].mDiffuse.r !=
|
if (mScene->mMaterials[i].mDiffuse.r !=
|
||||||
mScene->mMaterials[i].mDiffuse.g ||
|
mScene->mMaterials[i].mDiffuse.g ||
|
||||||
mScene->mMaterials[i].mDiffuse.r !=
|
mScene->mMaterials[i].mDiffuse.r !=
|
||||||
mScene->mMaterials[i].mDiffuse.b)continue;
|
mScene->mMaterials[i].mDiffuse.b) continue;
|
||||||
|
|
||||||
if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
|
if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
|
||||||
mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
|
mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
|
||||||
mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
|
mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
|
||||||
mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
|
mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
|
||||||
mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
|
mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
|
||||||
mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
|
mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
if ( NotSet == idx ) {
|
if (NotSet == idx) {
|
||||||
idx = ( unsigned int )mScene->mMaterials.size();
|
idx = (unsigned int)mScene->mMaterials.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// now iterate through all meshes and through all faces and
|
// now iterate through all meshes and through all faces and
|
||||||
// find all faces that are using the default material
|
// find all faces that are using the default material
|
||||||
unsigned int cnt = 0;
|
unsigned int cnt = 0;
|
||||||
for (std::vector<D3DS::Mesh>::iterator
|
for (std::vector<D3DS::Mesh>::iterator
|
||||||
i = mScene->mMeshes.begin();
|
i = mScene->mMeshes.begin();
|
||||||
i != mScene->mMeshes.end();++i)
|
i != mScene->mMeshes.end(); ++i) {
|
||||||
{
|
|
||||||
for (std::vector<unsigned int>::iterator
|
for (std::vector<unsigned int>::iterator
|
||||||
a = (*i).mFaceMaterials.begin();
|
a = (*i).mFaceMaterials.begin();
|
||||||
a != (*i).mFaceMaterials.end();++a)
|
a != (*i).mFaceMaterials.end(); ++a) {
|
||||||
{
|
|
||||||
// NOTE: The additional check seems to be necessary,
|
// NOTE: The additional check seems to be necessary,
|
||||||
// some exporters seem to generate invalid data here
|
// some exporters seem to generate invalid data here
|
||||||
if (0xcdcdcdcd == (*a))
|
if (0xcdcdcdcd == (*a)) {
|
||||||
{
|
|
||||||
(*a) = idx;
|
(*a) = idx;
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
} else if ((*a) >= mScene->mMaterials.size()) {
|
||||||
else if ( (*a) >= mScene->mMaterials.size())
|
|
||||||
{
|
|
||||||
(*a) = idx;
|
(*a) = idx;
|
||||||
ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
|
ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cnt && idx == mScene->mMaterials.size())
|
if (cnt && idx == mScene->mMaterials.size()) {
|
||||||
{
|
|
||||||
// We need to create our own default material
|
// We need to create our own default material
|
||||||
D3DS::Material sMat("%%%DEFAULT");
|
D3DS::Material sMat("%%%DEFAULT");
|
||||||
sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
|
sMat.mDiffuse = aiColor3D(0.3f, 0.3f, 0.3f);
|
||||||
mScene->mMaterials.push_back(sMat);
|
mScene->mMaterials.push_back(sMat);
|
||||||
|
|
||||||
ASSIMP_LOG_INFO("3DS: Generating default material");
|
ASSIMP_LOG_INFO("3DS: Generating default material");
|
||||||
|
@ -135,22 +126,17 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
|
// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
|
||||||
void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
|
void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
|
||||||
{
|
for (std::vector<D3DS::Face>::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
|
||||||
for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
|
|
||||||
{
|
|
||||||
// check whether all indices are in range
|
// check whether all indices are in range
|
||||||
for (unsigned int a = 0; a < 3;++a)
|
for (unsigned int a = 0; a < 3; ++a) {
|
||||||
{
|
if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
|
||||||
if ((*i).mIndices[a] >= sMesh.mPositions.size())
|
|
||||||
{
|
|
||||||
ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
|
ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
|
||||||
(*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
|
(*i).mIndices[a] = (uint32_t)sMesh.mPositions.size() - 1;
|
||||||
}
|
}
|
||||||
if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
|
if (!sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) {
|
||||||
{
|
|
||||||
ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
|
ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
|
||||||
(*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
|
(*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size() - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,24 +144,21 @@ void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Generate out unique verbose format representation
|
// Generate out unique verbose format representation
|
||||||
void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
|
void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
|
||||||
{
|
|
||||||
// TODO: really necessary? I don't think. Just a waste of memory and time
|
// TODO: really necessary? I don't think. Just a waste of memory and time
|
||||||
// to do it now in a separate buffer.
|
// to do it now in a separate buffer.
|
||||||
|
|
||||||
// Allocate output storage
|
// Allocate output storage
|
||||||
std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
|
std::vector<aiVector3D> vNew(sMesh.mFaces.size() * 3);
|
||||||
std::vector<aiVector3D> vNew2;
|
std::vector<aiVector3D> vNew2;
|
||||||
if (sMesh.mTexCoords.size())
|
if (sMesh.mTexCoords.size())
|
||||||
vNew2.resize(sMesh.mFaces.size() * 3);
|
vNew2.resize(sMesh.mFaces.size() * 3);
|
||||||
|
|
||||||
for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
|
for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
|
||||||
{
|
D3DS::Face &face = sMesh.mFaces[i];
|
||||||
D3DS::Face& face = sMesh.mFaces[i];
|
|
||||||
|
|
||||||
// Positions
|
// Positions
|
||||||
for (unsigned int a = 0; a < 3;++a,++base)
|
for (unsigned int a = 0; a < 3; ++a, ++base) {
|
||||||
{
|
|
||||||
vNew[base] = sMesh.mPositions[face.mIndices[a]];
|
vNew[base] = sMesh.mPositions[face.mIndices[a]];
|
||||||
if (sMesh.mTexCoords.size())
|
if (sMesh.mTexCoords.size())
|
||||||
vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
|
vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
|
||||||
|
@ -189,26 +172,24 @@ void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Convert a 3DS texture to texture keys in an aiMaterial
|
// Convert a 3DS texture to texture keys in an aiMaterial
|
||||||
void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
|
void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
|
||||||
{
|
|
||||||
// Setup the texture name
|
// Setup the texture name
|
||||||
aiString tex;
|
aiString tex;
|
||||||
tex.Set( texture.mMapName);
|
tex.Set(texture.mMapName);
|
||||||
mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
|
mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
|
||||||
|
|
||||||
// Setup the texture blend factor
|
// Setup the texture blend factor
|
||||||
if (is_not_qnan(texture.mTextureBlend))
|
if (is_not_qnan(texture.mTextureBlend))
|
||||||
mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
|
mat.AddProperty<ai_real>(&texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type, 0));
|
||||||
|
|
||||||
// Setup the texture mapping mode
|
// Setup the texture mapping mode
|
||||||
int mapMode = static_cast<int>(texture.mMapMode);
|
int mapMode = static_cast<int>(texture.mMapMode);
|
||||||
mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
|
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0));
|
||||||
mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
|
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0));
|
||||||
|
|
||||||
// Mirroring - double the scaling values
|
// Mirroring - double the scaling values
|
||||||
// FIXME: this is not really correct ...
|
// FIXME: this is not really correct ...
|
||||||
if (texture.mMapMode == aiTextureMapMode_Mirror)
|
if (texture.mMapMode == aiTextureMapMode_Mirror) {
|
||||||
{
|
|
||||||
texture.mScaleU *= 2.0;
|
texture.mScaleU *= 2.0;
|
||||||
texture.mScaleV *= 2.0;
|
texture.mScaleV *= 2.0;
|
||||||
texture.mOffsetU /= 2.0;
|
texture.mOffsetU /= 2.0;
|
||||||
|
@ -216,21 +197,19 @@ void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup texture UV transformations
|
// Setup texture UV transformations
|
||||||
mat.AddProperty<ai_real>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
|
mat.AddProperty<ai_real>(&texture.mOffsetU, 5, AI_MATKEY_UVTRANSFORM(type, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Convert a 3DS material to an aiMaterial
|
// Convert a 3DS material to an aiMaterial
|
||||||
void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
|
void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
|
||||||
aiMaterial& mat)
|
aiMaterial &mat) {
|
||||||
{
|
|
||||||
// NOTE: Pass the background image to the viewer by bypassing the
|
// NOTE: Pass the background image to the viewer by bypassing the
|
||||||
// material system. This is an evil hack, never do it again!
|
// material system. This is an evil hack, never do it again!
|
||||||
if (0 != mBackgroundImage.length() && bHasBG)
|
if (0 != mBackgroundImage.length() && bHasBG) {
|
||||||
{
|
|
||||||
aiString tex;
|
aiString tex;
|
||||||
tex.Set( mBackgroundImage);
|
tex.Set(mBackgroundImage);
|
||||||
mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
|
mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
|
||||||
|
|
||||||
// Be sure this is only done for the first material
|
// Be sure this is only done for the first material
|
||||||
mBackgroundImage = std::string("");
|
mBackgroundImage = std::string("");
|
||||||
|
@ -242,143 +221,138 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
|
||||||
oldMat.mAmbient.b += mClrAmbient.b;
|
oldMat.mAmbient.b += mClrAmbient.b;
|
||||||
|
|
||||||
aiString name;
|
aiString name;
|
||||||
name.Set( oldMat.mName);
|
name.Set(oldMat.mName);
|
||||||
mat.AddProperty( &name, AI_MATKEY_NAME);
|
mat.AddProperty(&name, AI_MATKEY_NAME);
|
||||||
|
|
||||||
// Material colors
|
// Material colors
|
||||||
mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
mat.AddProperty(&oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
||||||
mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
mat.AddProperty(&oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
mat.AddProperty(&oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
||||||
mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
||||||
|
|
||||||
// Phong shininess and shininess strength
|
// Phong shininess and shininess strength
|
||||||
if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
|
if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
|
||||||
D3DS::Discreet3DS::Metal == oldMat.mShading)
|
D3DS::Discreet3DS::Metal == oldMat.mShading) {
|
||||||
{
|
if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
|
||||||
if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
|
|
||||||
{
|
|
||||||
oldMat.mShading = D3DS::Discreet3DS::Gouraud;
|
oldMat.mShading = D3DS::Discreet3DS::Gouraud;
|
||||||
}
|
} else {
|
||||||
else
|
mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
|
||||||
{
|
mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
|
||||||
mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
|
|
||||||
mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opacity
|
// Opacity
|
||||||
mat.AddProperty<ai_real>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
|
mat.AddProperty<ai_real>(&oldMat.mTransparency, 1, AI_MATKEY_OPACITY);
|
||||||
|
|
||||||
// Bump height scaling
|
// Bump height scaling
|
||||||
mat.AddProperty<ai_real>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
|
mat.AddProperty<ai_real>(&oldMat.mBumpHeight, 1, AI_MATKEY_BUMPSCALING);
|
||||||
|
|
||||||
// Two sided rendering?
|
// Two sided rendering?
|
||||||
if (oldMat.mTwoSided)
|
if (oldMat.mTwoSided) {
|
||||||
{
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
|
mat.AddProperty<int>(&i, 1, AI_MATKEY_TWOSIDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shading mode
|
// Shading mode
|
||||||
aiShadingMode eShading = aiShadingMode_NoShading;
|
aiShadingMode eShading = aiShadingMode_NoShading;
|
||||||
switch (oldMat.mShading)
|
switch (oldMat.mShading) {
|
||||||
{
|
case D3DS::Discreet3DS::Flat:
|
||||||
case D3DS::Discreet3DS::Flat:
|
eShading = aiShadingMode_Flat;
|
||||||
eShading = aiShadingMode_Flat; break;
|
break;
|
||||||
|
|
||||||
// I don't know what "Wire" shading should be,
|
// I don't know what "Wire" shading should be,
|
||||||
// assume it is simple lambertian diffuse shading
|
// assume it is simple lambertian diffuse shading
|
||||||
case D3DS::Discreet3DS::Wire:
|
case D3DS::Discreet3DS::Wire: {
|
||||||
{
|
// Set the wireframe flag
|
||||||
// Set the wireframe flag
|
unsigned int iWire = 1;
|
||||||
unsigned int iWire = 1;
|
mat.AddProperty<int>((int *)&iWire, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
||||||
mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case D3DS::Discreet3DS::Gouraud:
|
case D3DS::Discreet3DS::Gouraud:
|
||||||
eShading = aiShadingMode_Gouraud; break;
|
eShading = aiShadingMode_Gouraud;
|
||||||
|
break;
|
||||||
|
|
||||||
// assume cook-torrance shading for metals.
|
// assume cook-torrance shading for metals.
|
||||||
case D3DS::Discreet3DS::Phong :
|
case D3DS::Discreet3DS::Phong:
|
||||||
eShading = aiShadingMode_Phong; break;
|
eShading = aiShadingMode_Phong;
|
||||||
|
break;
|
||||||
|
|
||||||
case D3DS::Discreet3DS::Metal :
|
case D3DS::Discreet3DS::Metal:
|
||||||
eShading = aiShadingMode_CookTorrance; break;
|
eShading = aiShadingMode_CookTorrance;
|
||||||
|
break;
|
||||||
|
|
||||||
// FIX to workaround a warning with GCC 4 who complained
|
// FIX to workaround a warning with GCC 4 who complained
|
||||||
// about a missing case Blinn: here - Blinn isn't a valid
|
// about a missing case Blinn: here - Blinn isn't a valid
|
||||||
// value in the 3DS Loader, it is just needed for ASE
|
// value in the 3DS Loader, it is just needed for ASE
|
||||||
case D3DS::Discreet3DS::Blinn :
|
case D3DS::Discreet3DS::Blinn:
|
||||||
eShading = aiShadingMode_Blinn; break;
|
eShading = aiShadingMode_Blinn;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
int eShading_ = static_cast<int>(eShading);
|
int eShading_ = static_cast<int>(eShading);
|
||||||
mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
|
mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
|
||||||
|
|
||||||
// DIFFUSE texture
|
// DIFFUSE texture
|
||||||
if( oldMat.sTexDiffuse.mMapName.length() > 0)
|
if (oldMat.sTexDiffuse.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
|
CopyTexture(mat, oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
|
||||||
|
|
||||||
// SPECULAR texture
|
// SPECULAR texture
|
||||||
if( oldMat.sTexSpecular.mMapName.length() > 0)
|
if (oldMat.sTexSpecular.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
|
CopyTexture(mat, oldMat.sTexSpecular, aiTextureType_SPECULAR);
|
||||||
|
|
||||||
// OPACITY texture
|
// OPACITY texture
|
||||||
if( oldMat.sTexOpacity.mMapName.length() > 0)
|
if (oldMat.sTexOpacity.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
|
CopyTexture(mat, oldMat.sTexOpacity, aiTextureType_OPACITY);
|
||||||
|
|
||||||
// EMISSIVE texture
|
// EMISSIVE texture
|
||||||
if( oldMat.sTexEmissive.mMapName.length() > 0)
|
if (oldMat.sTexEmissive.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
|
CopyTexture(mat, oldMat.sTexEmissive, aiTextureType_EMISSIVE);
|
||||||
|
|
||||||
// BUMP texture
|
// BUMP texture
|
||||||
if( oldMat.sTexBump.mMapName.length() > 0)
|
if (oldMat.sTexBump.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
|
CopyTexture(mat, oldMat.sTexBump, aiTextureType_HEIGHT);
|
||||||
|
|
||||||
// SHININESS texture
|
// SHININESS texture
|
||||||
if( oldMat.sTexShininess.mMapName.length() > 0)
|
if (oldMat.sTexShininess.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
|
CopyTexture(mat, oldMat.sTexShininess, aiTextureType_SHININESS);
|
||||||
|
|
||||||
// REFLECTION texture
|
// REFLECTION texture
|
||||||
if( oldMat.sTexReflective.mMapName.length() > 0)
|
if (oldMat.sTexReflective.mMapName.length() > 0)
|
||||||
CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
|
CopyTexture(mat, oldMat.sTexReflective, aiTextureType_REFLECTION);
|
||||||
|
|
||||||
// Store the name of the material itself, too
|
// Store the name of the material itself, too
|
||||||
if( oldMat.mName.length()) {
|
if (oldMat.mName.length()) {
|
||||||
aiString tex;
|
aiString tex;
|
||||||
tex.Set( oldMat.mName);
|
tex.Set(oldMat.mName);
|
||||||
mat.AddProperty( &tex, AI_MATKEY_NAME);
|
mat.AddProperty(&tex, AI_MATKEY_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Split meshes by their materials and generate output aiMesh'es
|
// Split meshes by their materials and generate output aiMesh'es
|
||||||
void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
void Discreet3DSImporter::ConvertMeshes(aiScene *pcOut) {
|
||||||
{
|
std::vector<aiMesh *> avOutMeshes;
|
||||||
std::vector<aiMesh*> avOutMeshes;
|
|
||||||
avOutMeshes.reserve(mScene->mMeshes.size() * 2);
|
avOutMeshes.reserve(mScene->mMeshes.size() * 2);
|
||||||
|
|
||||||
unsigned int iFaceCnt = 0,num = 0;
|
unsigned int iFaceCnt = 0, num = 0;
|
||||||
aiString name;
|
aiString name;
|
||||||
|
|
||||||
// we need to split all meshes by their materials
|
// we need to split all meshes by their materials
|
||||||
for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) {
|
for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end(); ++i) {
|
||||||
std::unique_ptr< std::vector<unsigned int>[] > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
|
std::unique_ptr<std::vector<unsigned int>[]> aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
|
||||||
|
|
||||||
name.length = ASSIMP_itoa10(name.data,num++);
|
name.length = ASSIMP_itoa10(name.data, num++);
|
||||||
|
|
||||||
unsigned int iNum = 0;
|
unsigned int iNum = 0;
|
||||||
for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
|
for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
|
||||||
a != (*i).mFaceMaterials.end();++a,++iNum)
|
a != (*i).mFaceMaterials.end(); ++a, ++iNum) {
|
||||||
{
|
|
||||||
aiSplit[*a].push_back(iNum);
|
aiSplit[*a].push_back(iNum);
|
||||||
}
|
}
|
||||||
// now generate submeshes
|
// now generate submeshes
|
||||||
for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
|
for (unsigned int p = 0; p < mScene->mMaterials.size(); ++p) {
|
||||||
{
|
|
||||||
if (aiSplit[p].empty()) {
|
if (aiSplit[p].empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
aiMesh* meshOut = new aiMesh();
|
aiMesh *meshOut = new aiMesh();
|
||||||
meshOut->mName = name;
|
meshOut->mName = name;
|
||||||
meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
||||||
|
|
||||||
|
@ -386,36 +360,33 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
||||||
meshOut->mMaterialIndex = p;
|
meshOut->mMaterialIndex = p;
|
||||||
|
|
||||||
// use the color data as temporary storage
|
// use the color data as temporary storage
|
||||||
meshOut->mColors[0] = (aiColor4D*)(&*i);
|
meshOut->mColors[0] = (aiColor4D *)(&*i);
|
||||||
avOutMeshes.push_back(meshOut);
|
avOutMeshes.push_back(meshOut);
|
||||||
|
|
||||||
// convert vertices
|
// convert vertices
|
||||||
meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
|
meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
|
||||||
meshOut->mNumVertices = meshOut->mNumFaces*3;
|
meshOut->mNumVertices = meshOut->mNumFaces * 3;
|
||||||
|
|
||||||
// allocate enough storage for faces
|
// allocate enough storage for faces
|
||||||
meshOut->mFaces = new aiFace[meshOut->mNumFaces];
|
meshOut->mFaces = new aiFace[meshOut->mNumFaces];
|
||||||
iFaceCnt += meshOut->mNumFaces;
|
iFaceCnt += meshOut->mNumFaces;
|
||||||
|
|
||||||
meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
|
meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
|
||||||
meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
|
meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
|
||||||
if ((*i).mTexCoords.size())
|
if ((*i).mTexCoords.size()) {
|
||||||
{
|
|
||||||
meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
|
meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
|
||||||
}
|
}
|
||||||
for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
|
for (unsigned int q = 0, base = 0; q < aiSplit[p].size(); ++q) {
|
||||||
{
|
|
||||||
unsigned int index = aiSplit[p][q];
|
unsigned int index = aiSplit[p][q];
|
||||||
aiFace& face = meshOut->mFaces[q];
|
aiFace &face = meshOut->mFaces[q];
|
||||||
|
|
||||||
face.mIndices = new unsigned int[3];
|
face.mIndices = new unsigned int[3];
|
||||||
face.mNumIndices = 3;
|
face.mNumIndices = 3;
|
||||||
|
|
||||||
for (unsigned int a = 0; a < 3;++a,++base)
|
for (unsigned int a = 0; a < 3; ++a, ++base) {
|
||||||
{
|
|
||||||
unsigned int idx = (*i).mFaces[index].mIndices[a];
|
unsigned int idx = (*i).mFaces[index].mIndices[a];
|
||||||
meshOut->mVertices[base] = (*i).mPositions[idx];
|
meshOut->mVertices[base] = (*i).mPositions[idx];
|
||||||
meshOut->mNormals [base] = (*i).mNormals[idx];
|
meshOut->mNormals[base] = (*i).mNormals[idx];
|
||||||
|
|
||||||
if ((*i).mTexCoords.size())
|
if ((*i).mTexCoords.size())
|
||||||
meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
|
meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
|
||||||
|
@ -428,8 +399,8 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
||||||
|
|
||||||
// Copy them to the output array
|
// Copy them to the output array
|
||||||
pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
|
pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
|
||||||
pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
|
pcOut->mMeshes = new aiMesh *[pcOut->mNumMeshes]();
|
||||||
for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
|
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
|
||||||
pcOut->mMeshes[a] = avOutMeshes[a];
|
pcOut->mMeshes[a] = avOutMeshes[a];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,47 +412,44 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Add a node to the scenegraph and setup its final transformation
|
// Add a node to the scenegraph and setup its final transformation
|
||||||
void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
|
||||||
D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/)
|
D3DS::Node *pcIn, aiMatrix4x4 & /*absTrafo*/) {
|
||||||
{
|
|
||||||
std::vector<unsigned int> iArray;
|
std::vector<unsigned int> iArray;
|
||||||
iArray.reserve(3);
|
iArray.reserve(3);
|
||||||
|
|
||||||
aiMatrix4x4 abs;
|
aiMatrix4x4 abs;
|
||||||
|
|
||||||
// Find all meshes with the same name as the node
|
// Find all meshes with the same name as the node
|
||||||
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
|
for (unsigned int a = 0; a < pcSOut->mNumMeshes; ++a) {
|
||||||
{
|
const D3DS::Mesh *pcMesh = (const D3DS::Mesh *)pcSOut->mMeshes[a]->mColors[0];
|
||||||
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
|
ai_assert(nullptr != pcMesh);
|
||||||
ai_assert(NULL != pcMesh);
|
|
||||||
|
|
||||||
if (pcIn->mName == pcMesh->mName)
|
if (pcIn->mName == pcMesh->mName)
|
||||||
iArray.push_back(a);
|
iArray.push_back(a);
|
||||||
}
|
}
|
||||||
if (!iArray.empty())
|
if (!iArray.empty()) {
|
||||||
{
|
|
||||||
// The matrix should be identical for all meshes with the
|
// The matrix should be identical for all meshes with the
|
||||||
// same name. It HAS to be identical for all meshes .....
|
// same name. It HAS to be identical for all meshes .....
|
||||||
D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
|
D3DS::Mesh *imesh = ((D3DS::Mesh *)pcSOut->mMeshes[iArray[0]]->mColors[0]);
|
||||||
|
|
||||||
// Compute the inverse of the transformation matrix to move the
|
// Compute the inverse of the transformation matrix to move the
|
||||||
// vertices back to their relative and local space
|
// vertices back to their relative and local space
|
||||||
aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
|
aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
|
||||||
mInv.Inverse();mInvTransposed.Transpose();
|
mInv.Inverse();
|
||||||
|
mInvTransposed.Transpose();
|
||||||
aiVector3D pivot = pcIn->vPivot;
|
aiVector3D pivot = pcIn->vPivot;
|
||||||
|
|
||||||
pcOut->mNumMeshes = (unsigned int)iArray.size();
|
pcOut->mNumMeshes = (unsigned int)iArray.size();
|
||||||
pcOut->mMeshes = new unsigned int[iArray.size()];
|
pcOut->mMeshes = new unsigned int[iArray.size()];
|
||||||
for (unsigned int i = 0;i < iArray.size();++i) {
|
for (unsigned int i = 0; i < iArray.size(); ++i) {
|
||||||
const unsigned int iIndex = iArray[i];
|
const unsigned int iIndex = iArray[i];
|
||||||
aiMesh* const mesh = pcSOut->mMeshes[iIndex];
|
aiMesh *const mesh = pcSOut->mMeshes[iIndex];
|
||||||
|
|
||||||
if (mesh->mColors[1] == NULL)
|
if (mesh->mColors[1] == nullptr) {
|
||||||
{
|
|
||||||
// Transform the vertices back into their local space
|
// Transform the vertices back into their local space
|
||||||
// fixme: consider computing normals after this, so we don't need to transform them
|
// fixme: consider computing normals after this, so we don't need to transform them
|
||||||
const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices;
|
const aiVector3D *const pvEnd = mesh->mVertices + mesh->mNumVertices;
|
||||||
aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
aiVector3D *pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
||||||
|
|
||||||
for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
||||||
*pvCurrent = mInv * (*pvCurrent);
|
*pvCurrent = mInv * (*pvCurrent);
|
||||||
|
@ -489,8 +457,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle negative transformation matrix determinant -> invert vertex x
|
// Handle negative transformation matrix determinant -> invert vertex x
|
||||||
if (imesh->mMat.Determinant() < 0.0f)
|
if (imesh->mMat.Determinant() < 0.0f) {
|
||||||
{
|
|
||||||
/* we *must* have normals */
|
/* we *must* have normals */
|
||||||
for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
||||||
pvCurrent->x *= -1.f;
|
pvCurrent->x *= -1.f;
|
||||||
|
@ -500,17 +467,15 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pivot point
|
// Handle pivot point
|
||||||
if (pivot.x || pivot.y || pivot.z)
|
if (pivot.x || pivot.y || pivot.z) {
|
||||||
{
|
for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
|
||||||
for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
|
|
||||||
*pvCurrent -= pivot;
|
*pvCurrent -= pivot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->mColors[1] = (aiColor4D*)1;
|
mesh->mColors[1] = (aiColor4D *)1;
|
||||||
}
|
} else
|
||||||
else
|
mesh->mColors[1] = (aiColor4D *)1;
|
||||||
mesh->mColors[1] = (aiColor4D*)1;
|
|
||||||
|
|
||||||
// Setup the mesh index
|
// Setup the mesh index
|
||||||
pcOut->mMeshes[i] = iIndex;
|
pcOut->mMeshes[i] = iIndex;
|
||||||
|
@ -519,78 +484,75 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
|
|
||||||
// Setup the name of the node
|
// Setup the name of the node
|
||||||
// First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
|
// First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
|
||||||
if (pcIn->mInstanceNumber > 1)
|
if (pcIn->mInstanceNumber > 1) {
|
||||||
{
|
|
||||||
char tmp[12];
|
char tmp[12];
|
||||||
ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
|
ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
|
||||||
std::string tempStr = pcIn->mName + "_inst_";
|
std::string tempStr = pcIn->mName + "_inst_";
|
||||||
tempStr += tmp;
|
tempStr += tmp;
|
||||||
pcOut->mName.Set(tempStr);
|
pcOut->mName.Set(tempStr);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
pcOut->mName.Set(pcIn->mName);
|
pcOut->mName.Set(pcIn->mName);
|
||||||
|
|
||||||
// Now build the transformation matrix of the node
|
// Now build the transformation matrix of the node
|
||||||
// ROTATION
|
// ROTATION
|
||||||
if (pcIn->aRotationKeys.size()){
|
if (pcIn->aRotationKeys.size()) {
|
||||||
|
|
||||||
// FIX to get to Assimp's quaternion conventions
|
// FIX to get to Assimp's quaternion conventions
|
||||||
for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
|
for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
|
||||||
(*it).mValue.w *= -1.f;
|
(*it).mValue.w *= -1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
|
pcOut->mTransformation = aiMatrix4x4(pcIn->aRotationKeys[0].mValue.GetMatrix());
|
||||||
}
|
} else if (pcIn->aCameraRollKeys.size()) {
|
||||||
else if (pcIn->aCameraRollKeys.size())
|
aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(-pcIn->aCameraRollKeys[0].mValue),
|
||||||
{
|
pcOut->mTransformation);
|
||||||
aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
|
|
||||||
pcOut->mTransformation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCALING
|
// SCALING
|
||||||
aiMatrix4x4& m = pcOut->mTransformation;
|
aiMatrix4x4 &m = pcOut->mTransformation;
|
||||||
if (pcIn->aScalingKeys.size())
|
if (pcIn->aScalingKeys.size()) {
|
||||||
{
|
const aiVector3D &v = pcIn->aScalingKeys[0].mValue;
|
||||||
const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
|
m.a1 *= v.x;
|
||||||
m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
|
m.b1 *= v.x;
|
||||||
m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
|
m.c1 *= v.x;
|
||||||
m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
|
m.a2 *= v.y;
|
||||||
|
m.b2 *= v.y;
|
||||||
|
m.c2 *= v.y;
|
||||||
|
m.a3 *= v.z;
|
||||||
|
m.b3 *= v.z;
|
||||||
|
m.c3 *= v.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TRANSLATION
|
// TRANSLATION
|
||||||
if (pcIn->aPositionKeys.size())
|
if (pcIn->aPositionKeys.size()) {
|
||||||
{
|
const aiVector3D &v = pcIn->aPositionKeys[0].mValue;
|
||||||
const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
|
|
||||||
m.a4 += v.x;
|
m.a4 += v.x;
|
||||||
m.b4 += v.y;
|
m.b4 += v.y;
|
||||||
m.c4 += v.z;
|
m.c4 += v.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate animation channels for the node
|
// Generate animation channels for the node
|
||||||
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
|
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
|
||||||
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
|
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
|
||||||
pcIn->aTargetPositionKeys.size() > 1)
|
pcIn->aTargetPositionKeys.size() > 1) {
|
||||||
{
|
aiAnimation *anim = pcSOut->mAnimations[0];
|
||||||
aiAnimation* anim = pcSOut->mAnimations[0];
|
|
||||||
ai_assert(nullptr != anim);
|
ai_assert(nullptr != anim);
|
||||||
|
|
||||||
if (pcIn->aCameraRollKeys.size() > 1)
|
if (pcIn->aCameraRollKeys.size() > 1) {
|
||||||
{
|
|
||||||
ASSIMP_LOG_DEBUG("3DS: Converting camera roll track ...");
|
ASSIMP_LOG_DEBUG("3DS: Converting camera roll track ...");
|
||||||
|
|
||||||
// Camera roll keys - in fact they're just rotations
|
// Camera roll keys - in fact they're just rotations
|
||||||
// around the camera's z axis. The angles are given
|
// around the camera's z axis. The angles are given
|
||||||
// in degrees (and they're clockwise).
|
// in degrees (and they're clockwise).
|
||||||
pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
|
pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
|
||||||
for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
|
for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size(); ++i) {
|
||||||
{
|
aiQuatKey &q = pcIn->aRotationKeys[i];
|
||||||
aiQuatKey& q = pcIn->aRotationKeys[i];
|
aiFloatKey &f = pcIn->aCameraRollKeys[i];
|
||||||
aiFloatKey& f = pcIn->aCameraRollKeys[i];
|
|
||||||
|
|
||||||
q.mTime = f.mTime;
|
q.mTime = f.mTime;
|
||||||
|
|
||||||
// FIX to get to Assimp quaternion conventions
|
// FIX to get to Assimp quaternion conventions
|
||||||
q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
|
q.mValue = aiQuaternion(0.f, 0.f, AI_DEG_TO_RAD(/*-*/ f.mValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -636,102 +598,93 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
// Cameras or lights define their transformation in their parent node and in the
|
// Cameras or lights define their transformation in their parent node and in the
|
||||||
// corresponding light or camera chunks. However, we read and process the latter
|
// corresponding light or camera chunks. However, we read and process the latter
|
||||||
// to to be able to return valid cameras/lights even if no scenegraph is given.
|
// to to be able to return valid cameras/lights even if no scenegraph is given.
|
||||||
for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) {
|
for (unsigned int n = 0; n < pcSOut->mNumCameras; ++n) {
|
||||||
if (pcSOut->mCameras[n]->mName == pcOut->mName) {
|
if (pcSOut->mCameras[n]->mName == pcOut->mName) {
|
||||||
pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
|
pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (unsigned int n = 0; n < pcSOut->mNumLights;++n) {
|
for (unsigned int n = 0; n < pcSOut->mNumLights; ++n) {
|
||||||
if (pcSOut->mLights[n]->mName == pcOut->mName) {
|
if (pcSOut->mLights[n]->mName == pcOut->mName) {
|
||||||
pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
|
pcSOut->mLights[n]->mDirection = aiVector3D(0.f, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a new node anim and setup its name
|
// Allocate a new node anim and setup its name
|
||||||
aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
|
aiNodeAnim *nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
|
||||||
nda->mNodeName.Set(pcIn->mName);
|
nda->mNodeName.Set(pcIn->mName);
|
||||||
|
|
||||||
// POSITION keys
|
// POSITION keys
|
||||||
if (pcIn->aPositionKeys.size() > 0)
|
if (pcIn->aPositionKeys.size() > 0) {
|
||||||
{
|
|
||||||
nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
|
nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
|
||||||
nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
|
nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
|
||||||
::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
|
::memcpy(nda->mPositionKeys, &pcIn->aPositionKeys[0],
|
||||||
sizeof(aiVectorKey)*nda->mNumPositionKeys);
|
sizeof(aiVectorKey) * nda->mNumPositionKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROTATION keys
|
// ROTATION keys
|
||||||
if (pcIn->aRotationKeys.size() > 0)
|
if (pcIn->aRotationKeys.size() > 0) {
|
||||||
{
|
|
||||||
nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
|
nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
|
||||||
nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
|
nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
|
||||||
|
|
||||||
// Rotations are quaternion offsets
|
// Rotations are quaternion offsets
|
||||||
aiQuaternion abs1;
|
aiQuaternion abs1;
|
||||||
for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
|
for (unsigned int n = 0; n < nda->mNumRotationKeys; ++n) {
|
||||||
{
|
const aiQuatKey &q = pcIn->aRotationKeys[n];
|
||||||
const aiQuatKey& q = pcIn->aRotationKeys[n];
|
|
||||||
|
|
||||||
abs1 = (n ? abs1 * q.mValue : q.mValue);
|
abs1 = (n ? abs1 * q.mValue : q.mValue);
|
||||||
nda->mRotationKeys[n].mTime = q.mTime;
|
nda->mRotationKeys[n].mTime = q.mTime;
|
||||||
nda->mRotationKeys[n].mValue = abs1.Normalize();
|
nda->mRotationKeys[n].mValue = abs1.Normalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCALING keys
|
// SCALING keys
|
||||||
if (pcIn->aScalingKeys.size() > 0)
|
if (pcIn->aScalingKeys.size() > 0) {
|
||||||
{
|
|
||||||
nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
|
nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
|
||||||
nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
|
nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
|
||||||
::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
|
::memcpy(nda->mScalingKeys, &pcIn->aScalingKeys[0],
|
||||||
sizeof(aiVectorKey)*nda->mNumScalingKeys);
|
sizeof(aiVectorKey) * nda->mNumScalingKeys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate storage for children
|
// Allocate storage for children
|
||||||
pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
|
pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
|
||||||
pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
|
pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
|
||||||
|
|
||||||
// Recursively process all children
|
// Recursively process all children
|
||||||
const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
|
const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
|
||||||
for (unsigned int i = 0; i < size;++i)
|
for (unsigned int i = 0; i < size; ++i) {
|
||||||
{
|
|
||||||
pcOut->mChildren[i] = new aiNode();
|
pcOut->mChildren[i] = new aiNode();
|
||||||
pcOut->mChildren[i]->mParent = pcOut;
|
pcOut->mChildren[i]->mParent = pcOut;
|
||||||
AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
|
AddNodeToGraph(pcSOut, pcOut->mChildren[i], pcIn->mChildren[i], abs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Find out how many node animation channels we'll have finally
|
// Find out how many node animation channels we'll have finally
|
||||||
void CountTracks(D3DS::Node* node, unsigned int& cnt)
|
void CountTracks(D3DS::Node *node, unsigned int &cnt) {
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// We will never generate more than one channel for a node, so
|
// We will never generate more than one channel for a node, so
|
||||||
// this is rather easy here.
|
// this is rather easy here.
|
||||||
|
|
||||||
if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
|
if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
|
||||||
node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
|
node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
|
||||||
node->aTargetPositionKeys.size() > 1)
|
node->aTargetPositionKeys.size() > 1) {
|
||||||
{
|
|
||||||
++cnt;
|
++cnt;
|
||||||
|
|
||||||
// account for the additional channel for the camera/spotlight target position
|
// account for the additional channel for the camera/spotlight target position
|
||||||
if (node->aTargetPositionKeys.size() > 1)++cnt;
|
if (node->aTargetPositionKeys.size() > 1) ++cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively process all children
|
// Recursively process all children
|
||||||
for (unsigned int i = 0; i < node->mChildren.size();++i)
|
for (unsigned int i = 0; i < node->mChildren.size(); ++i)
|
||||||
CountTracks(node->mChildren[i],cnt);
|
CountTracks(node->mChildren[i], cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Generate the output node graph
|
// Generate the output node graph
|
||||||
void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
|
||||||
{
|
|
||||||
pcOut->mRootNode = new aiNode();
|
pcOut->mRootNode = new aiNode();
|
||||||
if (0 == mRootNode->mChildren.size())
|
if (0 == mRootNode->mChildren.size()) {
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// It seems the file is so messed up that it has not even a hierarchy.
|
// It seems the file is so messed up that it has not even a hierarchy.
|
||||||
// generate a flat hiearachy which looks like this:
|
// generate a flat hiearachy which looks like this:
|
||||||
|
@ -745,29 +698,27 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
||||||
ASSIMP_LOG_WARN("No hierarchy information has been found in the file. ");
|
ASSIMP_LOG_WARN("No hierarchy information has been found in the file. ");
|
||||||
|
|
||||||
pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
|
pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
|
||||||
static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
|
static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
|
||||||
|
|
||||||
pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
|
pcOut->mRootNode->mChildren = new aiNode *[pcOut->mRootNode->mNumChildren];
|
||||||
pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
|
pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
|
||||||
|
|
||||||
// Build dummy nodes for all meshes
|
// Build dummy nodes for all meshes
|
||||||
unsigned int a = 0;
|
unsigned int a = 0;
|
||||||
for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
|
for (unsigned int i = 0; i < pcOut->mNumMeshes; ++i, ++a) {
|
||||||
{
|
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
|
||||||
pcNode->mParent = pcOut->mRootNode;
|
pcNode->mParent = pcOut->mRootNode;
|
||||||
pcNode->mMeshes = new unsigned int[1];
|
pcNode->mMeshes = new unsigned int[1];
|
||||||
pcNode->mMeshes[0] = i;
|
pcNode->mMeshes[0] = i;
|
||||||
pcNode->mNumMeshes = 1;
|
pcNode->mNumMeshes = 1;
|
||||||
|
|
||||||
// Build a name for the node
|
// Build a name for the node
|
||||||
pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u",i);
|
pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build dummy nodes for all cameras
|
// Build dummy nodes for all cameras
|
||||||
for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
|
for (unsigned int i = 0; i < (unsigned int)mScene->mCameras.size(); ++i, ++a) {
|
||||||
{
|
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
|
||||||
pcNode->mParent = pcOut->mRootNode;
|
pcNode->mParent = pcOut->mRootNode;
|
||||||
|
|
||||||
// Build a name for the node
|
// Build a name for the node
|
||||||
|
@ -775,75 +726,68 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build dummy nodes for all lights
|
// Build dummy nodes for all lights
|
||||||
for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
|
for (unsigned int i = 0; i < (unsigned int)mScene->mLights.size(); ++i, ++a) {
|
||||||
{
|
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
|
||||||
pcNode->mParent = pcOut->mRootNode;
|
pcNode->mParent = pcOut->mRootNode;
|
||||||
|
|
||||||
// Build a name for the node
|
// Build a name for the node
|
||||||
pcNode->mName = mScene->mLights[i]->mName;
|
pcNode->mName = mScene->mLights[i]->mName;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// First of all: find out how many scaling, rotation and translation
|
// First of all: find out how many scaling, rotation and translation
|
||||||
// animation tracks we'll have afterwards
|
// animation tracks we'll have afterwards
|
||||||
unsigned int numChannel = 0;
|
unsigned int numChannel = 0;
|
||||||
CountTracks(mRootNode,numChannel);
|
CountTracks(mRootNode, numChannel);
|
||||||
|
|
||||||
if (numChannel)
|
if (numChannel) {
|
||||||
{
|
|
||||||
// Allocate a primary animation channel
|
// Allocate a primary animation channel
|
||||||
pcOut->mNumAnimations = 1;
|
pcOut->mNumAnimations = 1;
|
||||||
pcOut->mAnimations = new aiAnimation*[1];
|
pcOut->mAnimations = new aiAnimation *[1];
|
||||||
aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
|
aiAnimation *anim = pcOut->mAnimations[0] = new aiAnimation();
|
||||||
|
|
||||||
anim->mName.Set("3DSMasterAnim");
|
anim->mName.Set("3DSMasterAnim");
|
||||||
|
|
||||||
// Allocate enough storage for all node animation channels,
|
// Allocate enough storage for all node animation channels,
|
||||||
// but don't set the mNumChannels member - we'll use it to
|
// but don't set the mNumChannels member - we'll use it to
|
||||||
// index into the array
|
// index into the array
|
||||||
anim->mChannels = new aiNodeAnim*[numChannel];
|
anim->mChannels = new aiNodeAnim *[numChannel];
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMatrix4x4 m;
|
aiMatrix4x4 m;
|
||||||
AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m);
|
AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We used the first and second vertex color set to store some temporary values so we need to cleanup here
|
// We used the first and second vertex color set to store some temporary values so we need to cleanup here
|
||||||
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a)
|
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
|
||||||
{
|
pcOut->mMeshes[a]->mColors[0] = nullptr;
|
||||||
pcOut->mMeshes[a]->mColors[0] = NULL;
|
pcOut->mMeshes[a]->mColors[1] = nullptr;
|
||||||
pcOut->mMeshes[a]->mColors[1] = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pcOut->mRootNode->mTransformation = aiMatrix4x4(
|
pcOut->mRootNode->mTransformation = aiMatrix4x4(
|
||||||
1.f,0.f,0.f,0.f,
|
1.f, 0.f, 0.f, 0.f,
|
||||||
0.f,0.f,1.f,0.f,
|
0.f, 0.f, 1.f, 0.f,
|
||||||
0.f,-1.f,0.f,0.f,
|
0.f, -1.f, 0.f, 0.f,
|
||||||
0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
|
0.f, 0.f, 0.f, 1.f) *
|
||||||
|
pcOut->mRootNode->mTransformation;
|
||||||
|
|
||||||
// If the root node is unnamed name it "<3DSRoot>"
|
// If the root node is unnamed name it "<3DSRoot>"
|
||||||
if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
|
if (::strstr(pcOut->mRootNode->mName.data, "UNNAMED") ||
|
||||||
(pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
|
(pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')) {
|
||||||
{
|
|
||||||
pcOut->mRootNode->mName.Set("<3DSRoot>");
|
pcOut->mRootNode->mName.Set("<3DSRoot>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Convert all meshes in the scene and generate the final output scene.
|
// Convert all meshes in the scene and generate the final output scene.
|
||||||
void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
|
void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
|
||||||
{
|
|
||||||
// Allocate enough storage for all output materials
|
// Allocate enough storage for all output materials
|
||||||
pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
|
pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
|
||||||
pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
|
pcOut->mMaterials = new aiMaterial *[pcOut->mNumMaterials];
|
||||||
|
|
||||||
// ... and convert the 3DS materials to aiMaterial's
|
// ... and convert the 3DS materials to aiMaterial's
|
||||||
for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
|
for (unsigned int i = 0; i < pcOut->mNumMaterials; ++i) {
|
||||||
{
|
aiMaterial *pcNew = new aiMaterial();
|
||||||
aiMaterial* pcNew = new aiMaterial();
|
ConvertMaterial(mScene->mMaterials[i], *pcNew);
|
||||||
ConvertMaterial(mScene->mMaterials[i],*pcNew);
|
|
||||||
pcOut->mMaterials[i] = pcNew;
|
pcOut->mMaterials[i] = pcNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,18 +796,16 @@ void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
|
||||||
|
|
||||||
// Now copy all light sources to the output scene
|
// Now copy all light sources to the output scene
|
||||||
pcOut->mNumLights = (unsigned int)mScene->mLights.size();
|
pcOut->mNumLights = (unsigned int)mScene->mLights.size();
|
||||||
if (pcOut->mNumLights)
|
if (pcOut->mNumLights) {
|
||||||
{
|
pcOut->mLights = new aiLight *[pcOut->mNumLights];
|
||||||
pcOut->mLights = new aiLight*[pcOut->mNumLights];
|
::memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
|
||||||
::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now copy all cameras to the output scene
|
// Now copy all cameras to the output scene
|
||||||
pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
|
pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
|
||||||
if (pcOut->mNumCameras)
|
if (pcOut->mNumCameras) {
|
||||||
{
|
pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
|
||||||
pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
|
::memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
|
||||||
::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,120 +43,117 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#ifndef ASSIMP_BUILD_NO_EXPORT
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
|
||||||
|
|
||||||
#include "3DS/3DSExporter.h"
|
#include "AssetLib/3DS/3DSExporter.h"
|
||||||
#include "3DS/3DSLoader.h"
|
#include "AssetLib/3DS/3DSHelper.h"
|
||||||
#include "3DS/3DSHelper.h"
|
#include "AssetLib/3DS/3DSLoader.h"
|
||||||
#include "PostProcessing/SplitLargeMeshes.h"
|
#include "PostProcessing/SplitLargeMeshes.h"
|
||||||
|
|
||||||
#include <assimp/SceneCombiner.h>
|
#include <assimp/SceneCombiner.h>
|
||||||
#include <assimp/StringComparison.h>
|
#include <assimp/StringComparison.h>
|
||||||
#include <assimp/IOSystem.hpp>
|
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/Exporter.hpp>
|
#include <assimp/Exporter.hpp>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
using namespace D3DS;
|
using namespace D3DS;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Scope utility to write a 3DS file chunk.
|
// Scope utility to write a 3DS file chunk.
|
||||||
//
|
//
|
||||||
// Upon construction, the chunk header is written with the chunk type (flags)
|
// Upon construction, the chunk header is written with the chunk type (flags)
|
||||||
// filled out, but the chunk size left empty. Upon destruction, the correct chunk
|
// filled out, but the chunk size left empty. Upon destruction, the correct chunk
|
||||||
// size based on the then-position of the output stream cursor is filled in.
|
// size based on the then-position of the output stream cursor is filled in.
|
||||||
class ChunkWriter {
|
class ChunkWriter {
|
||||||
enum {
|
enum {
|
||||||
CHUNK_SIZE_NOT_SET = 0xdeadbeef
|
CHUNK_SIZE_NOT_SET = 0xdeadbeef,
|
||||||
, SIZE_OFFSET = 2
|
SIZE_OFFSET = 2
|
||||||
};
|
|
||||||
public:
|
|
||||||
|
|
||||||
ChunkWriter(StreamWriterLE& writer, uint16_t chunk_type)
|
|
||||||
: writer(writer)
|
|
||||||
{
|
|
||||||
chunk_start_pos = writer.GetCurrentPos();
|
|
||||||
writer.PutU2(chunk_type);
|
|
||||||
writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ChunkWriter() {
|
|
||||||
std::size_t head_pos = writer.GetCurrentPos();
|
|
||||||
|
|
||||||
ai_assert(head_pos > chunk_start_pos);
|
|
||||||
const std::size_t chunk_size = head_pos - chunk_start_pos;
|
|
||||||
|
|
||||||
writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
|
|
||||||
writer.PutU4(static_cast<uint32_t>(chunk_size));
|
|
||||||
writer.SetCurrentPos(head_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
StreamWriterLE& writer;
|
|
||||||
std::size_t chunk_start_pos;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
// Return an unique name for a given |mesh| attached to |node| that
|
ChunkWriter(StreamWriterLE &writer, uint16_t chunk_type) :
|
||||||
// preserves the mesh's given name if it has one. |index| is the index
|
writer(writer) {
|
||||||
// of the mesh in |aiScene::mMeshes|.
|
chunk_start_pos = writer.GetCurrentPos();
|
||||||
std::string GetMeshName(const aiMesh& mesh, unsigned int index, const aiNode& node) {
|
writer.PutU2(chunk_type);
|
||||||
static const std::string underscore = "_";
|
writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
|
||||||
char postfix[10] = {0};
|
|
||||||
ASSIMP_itoa10(postfix, index);
|
|
||||||
|
|
||||||
std::string result = node.mName.C_Str();
|
|
||||||
if (mesh.mName.length > 0) {
|
|
||||||
result += underscore + mesh.mName.C_Str();
|
|
||||||
}
|
|
||||||
return result + underscore + postfix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an unique name for a given |mat| with original position |index|
|
~ChunkWriter() {
|
||||||
// in |aiScene::mMaterials|. The name preserves the original material
|
std::size_t head_pos = writer.GetCurrentPos();
|
||||||
// name if possible.
|
|
||||||
std::string GetMaterialName(const aiMaterial& mat, unsigned int index) {
|
|
||||||
static const std::string underscore = "_";
|
|
||||||
char postfix[10] = {0};
|
|
||||||
ASSIMP_itoa10(postfix, index);
|
|
||||||
|
|
||||||
aiString mat_name;
|
ai_assert(head_pos > chunk_start_pos);
|
||||||
if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
|
const std::size_t chunk_size = head_pos - chunk_start_pos;
|
||||||
return mat_name.C_Str() + underscore + postfix;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Material" + underscore + postfix;
|
writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
|
||||||
|
writer.PutU4(static_cast<uint32_t>(chunk_size));
|
||||||
|
writer.SetCurrentPos(head_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect world transformations for each node
|
private:
|
||||||
void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
|
StreamWriterLE &writer;
|
||||||
const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
|
std::size_t chunk_start_pos;
|
||||||
trafos[node] = parent * node->mTransformation;
|
};
|
||||||
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
|
||||||
CollectTrafos(node->mChildren[i], trafos);
|
// Return an unique name for a given |mesh| attached to |node| that
|
||||||
}
|
// preserves the mesh's given name if it has one. |index| is the index
|
||||||
|
// of the mesh in |aiScene::mMeshes|.
|
||||||
|
std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
|
||||||
|
static const std::string underscore = "_";
|
||||||
|
char postfix[10] = { 0 };
|
||||||
|
ASSIMP_itoa10(postfix, index);
|
||||||
|
|
||||||
|
std::string result = node.mName.C_Str();
|
||||||
|
if (mesh.mName.length > 0) {
|
||||||
|
result += underscore + mesh.mName.C_Str();
|
||||||
|
}
|
||||||
|
return result + underscore + postfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an unique name for a given |mat| with original position |index|
|
||||||
|
// in |aiScene::mMaterials|. The name preserves the original material
|
||||||
|
// name if possible.
|
||||||
|
std::string GetMaterialName(const aiMaterial &mat, unsigned int index) {
|
||||||
|
static const std::string underscore = "_";
|
||||||
|
char postfix[10] = { 0 };
|
||||||
|
ASSIMP_itoa10(postfix, index);
|
||||||
|
|
||||||
|
aiString mat_name;
|
||||||
|
if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
|
||||||
|
return mat_name.C_Str() + underscore + postfix;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a flat list of the meshes (by index) assigned to each node
|
return "Material" + underscore + postfix;
|
||||||
void CollectMeshes(const aiNode* node, std::multimap<const aiNode*, unsigned int>& meshes) {
|
}
|
||||||
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
|
|
||||||
meshes.insert(std::make_pair(node, node->mMeshes[i]));
|
// Collect world transformations for each node
|
||||||
}
|
void CollectTrafos(const aiNode *node, std::map<const aiNode *, aiMatrix4x4> &trafos) {
|
||||||
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
const aiMatrix4x4 &parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
|
||||||
CollectMeshes(node->mChildren[i], meshes);
|
trafos[node] = parent * node->mTransformation;
|
||||||
}
|
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
||||||
|
CollectTrafos(node->mChildren[i], trafos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a flat list of the meshes (by index) assigned to each node
|
||||||
|
void CollectMeshes(const aiNode *node, std::multimap<const aiNode *, unsigned int> &meshes) {
|
||||||
|
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
|
||||||
|
meshes.insert(std::make_pair(node, node->mMeshes[i]));
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
||||||
|
CollectMeshes(node->mChildren[i], meshes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
|
// Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
|
||||||
void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
|
void ExportScene3DS(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
|
||||||
{
|
std::shared_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
|
||||||
std::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
|
if (!outfile) {
|
||||||
if(!outfile) {
|
|
||||||
throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
|
throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +164,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
|
||||||
// SplitLargeMeshes can do this, but it requires the correct limit to be set
|
// SplitLargeMeshes can do this, but it requires the correct limit to be set
|
||||||
// which is not possible with the current way of specifying preprocess steps
|
// which is not possible with the current way of specifying preprocess steps
|
||||||
// in |Exporter::ExportFormatEntry|.
|
// in |Exporter::ExportFormatEntry|.
|
||||||
aiScene* scenecopy_tmp;
|
aiScene *scenecopy_tmp;
|
||||||
SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
|
SceneCombiner::CopyScene(&scenecopy_tmp, pScene);
|
||||||
std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
|
std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
|
||||||
|
|
||||||
SplitLargeMeshesProcess_Triangle tri_splitter;
|
SplitLargeMeshesProcess_Triangle tri_splitter;
|
||||||
|
@ -186,10 +183,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* scene)
|
Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene *scene) :
|
||||||
: scene(scene)
|
scene(scene), writer(outfile) {
|
||||||
, writer(outfile)
|
|
||||||
{
|
|
||||||
CollectTrafos(scene->mRootNode, trafos);
|
CollectTrafos(scene->mRootNode, trafos);
|
||||||
CollectMeshes(scene->mRootNode, meshes);
|
CollectMeshes(scene->mRootNode, meshes);
|
||||||
|
|
||||||
|
@ -217,10 +212,8 @@ Discreet3DSExporter::~Discreet3DSExporter() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level)
|
int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
|
||||||
{
|
|
||||||
// 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
|
// 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
|
||||||
{
|
{
|
||||||
ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
|
ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
|
||||||
|
@ -237,7 +230,7 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
|
||||||
|
|
||||||
int16_t hierarchy_pos = static_cast<int16_t>(seq);
|
int16_t hierarchy_pos = static_cast<int16_t>(seq);
|
||||||
if (sibling_level != -1) {
|
if (sibling_level != -1) {
|
||||||
hierarchy_pos =(uint16_t) sibling_level;
|
hierarchy_pos = (uint16_t)sibling_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the hierarchy position
|
// Write the hierarchy position
|
||||||
|
@ -260,7 +253,7 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
|
||||||
const bool first_child = node.mNumChildren == 0 && i == 0;
|
const bool first_child = node.mNumChildren == 0 && i == 0;
|
||||||
|
|
||||||
const unsigned int mesh_idx = node.mMeshes[i];
|
const unsigned int mesh_idx = node.mMeshes[i];
|
||||||
const aiMesh& mesh = *scene->mMeshes[mesh_idx];
|
const aiMesh &mesh = *scene->mMeshes[mesh_idx];
|
||||||
|
|
||||||
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
|
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
|
||||||
{
|
{
|
||||||
|
@ -276,15 +269,14 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteMaterials()
|
void Discreet3DSExporter::WriteMaterials() {
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
|
for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
|
||||||
ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
|
ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
|
||||||
const aiMaterial& mat = *scene->mMaterials[i];
|
const aiMaterial &mat = *scene->mMaterials[i];
|
||||||
|
|
||||||
{
|
{
|
||||||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
|
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
|
||||||
const std::string& name = GetMaterialName(mat, i);
|
const std::string &name = GetMaterialName(mat, i);
|
||||||
WriteString(name);
|
WriteString(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +306,7 @@ void Discreet3DSExporter::WriteMaterials()
|
||||||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
|
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
|
||||||
|
|
||||||
Discreet3DS::shadetype3ds shading_mode_out;
|
Discreet3DS::shadetype3ds shading_mode_out;
|
||||||
switch(shading_mode) {
|
switch (shading_mode) {
|
||||||
case aiShadingMode_Flat:
|
case aiShadingMode_Flat:
|
||||||
case aiShadingMode_NoShading:
|
case aiShadingMode_NoShading:
|
||||||
shading_mode_out = Discreet3DS::Flat;
|
shading_mode_out = Discreet3DS::Flat;
|
||||||
|
@ -341,7 +333,6 @@ void Discreet3DSExporter::WriteMaterials()
|
||||||
writer.PutU2(static_cast<uint16_t>(shading_mode_out));
|
writer.PutU2(static_cast<uint16_t>(shading_mode_out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float f;
|
float f;
|
||||||
if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
|
if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
|
||||||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
|
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
|
||||||
|
@ -370,8 +361,7 @@ void Discreet3DSExporter::WriteMaterials()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags)
|
void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
|
||||||
{
|
|
||||||
aiString path;
|
aiString path;
|
||||||
aiTextureMapMode map_mode[2] = {
|
aiTextureMapMode map_mode[2] = {
|
||||||
aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
|
aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
|
||||||
|
@ -400,8 +390,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
|
||||||
uint16_t val = 0; // WRAP
|
uint16_t val = 0; // WRAP
|
||||||
if (map_mode[0] == aiTextureMapMode_Mirror) {
|
if (map_mode[0] == aiTextureMapMode_Mirror) {
|
||||||
val = 0x2;
|
val = 0x2;
|
||||||
}
|
} else if (map_mode[0] == aiTextureMapMode_Decal) {
|
||||||
else if (map_mode[0] == aiTextureMapMode_Decal) {
|
|
||||||
val = 0x10;
|
val = 0x10;
|
||||||
}
|
}
|
||||||
writer.PutU2(val);
|
writer.PutU2(val);
|
||||||
|
@ -410,8 +399,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteMeshes()
|
void Discreet3DSExporter::WriteMeshes() {
|
||||||
{
|
|
||||||
// NOTE: 3DS allows for instances. However:
|
// NOTE: 3DS allows for instances. However:
|
||||||
// i) not all importers support reading them
|
// i) not all importers support reading them
|
||||||
// ii) instances are not as flexible as they are in assimp, in particular,
|
// ii) instances are not as flexible as they are in assimp, in particular,
|
||||||
|
@ -423,25 +411,24 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
// Furthermore, the TRIMESH is transformed into world space so that it will
|
// Furthermore, the TRIMESH is transformed into world space so that it will
|
||||||
// appear correctly if importers don't read the scene hierarchy at all.
|
// appear correctly if importers don't read the scene hierarchy at all.
|
||||||
for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
|
for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
|
||||||
const aiNode& node = *(*it).first;
|
const aiNode &node = *(*it).first;
|
||||||
const unsigned int mesh_idx = (*it).second;
|
const unsigned int mesh_idx = (*it).second;
|
||||||
|
|
||||||
const aiMesh& mesh = *scene->mMeshes[mesh_idx];
|
const aiMesh &mesh = *scene->mMeshes[mesh_idx];
|
||||||
|
|
||||||
// This should not happen if the SLM step is correctly executed
|
// This should not happen if the SLM step is correctly executed
|
||||||
// before the scene is handed to the exporter
|
// before the scene is handed to the exporter
|
||||||
ai_assert(mesh.mNumVertices <= 0xffff);
|
ai_assert(mesh.mNumVertices <= 0xffff);
|
||||||
ai_assert(mesh.mNumFaces <= 0xffff);
|
ai_assert(mesh.mNumFaces <= 0xffff);
|
||||||
|
|
||||||
const aiMatrix4x4& trafo = trafos[&node];
|
const aiMatrix4x4 &trafo = trafos[&node];
|
||||||
|
|
||||||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
|
ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
|
||||||
|
|
||||||
// Mesh name is tied to the node it is attached to so it can later be referenced
|
// Mesh name is tied to the node it is attached to so it can later be referenced
|
||||||
const std::string& name = GetMeshName(mesh, mesh_idx, node);
|
const std::string &name = GetMeshName(mesh, mesh_idx, node);
|
||||||
WriteString(name);
|
WriteString(name);
|
||||||
|
|
||||||
|
|
||||||
// TRIMESH chunk
|
// TRIMESH chunk
|
||||||
ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
|
ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
|
||||||
|
|
||||||
|
@ -452,7 +439,7 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
|
const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
|
||||||
writer.PutU2(count);
|
writer.PutU2(count);
|
||||||
for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
|
for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
|
||||||
const aiVector3D& v = trafo * mesh.mVertices[i];
|
const aiVector3D &v = trafo * mesh.mVertices[i];
|
||||||
writer.PutF4(v.x);
|
writer.PutF4(v.x);
|
||||||
writer.PutF4(v.y);
|
writer.PutF4(v.y);
|
||||||
writer.PutF4(v.z);
|
writer.PutF4(v.z);
|
||||||
|
@ -466,7 +453,7 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
writer.PutU2(count);
|
writer.PutU2(count);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
|
for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
|
||||||
const aiVector3D& v = mesh.mTextureCoords[0][i];
|
const aiVector3D &v = mesh.mTextureCoords[0][i];
|
||||||
writer.PutF4(v.x);
|
writer.PutF4(v.x);
|
||||||
writer.PutF4(v.y);
|
writer.PutF4(v.y);
|
||||||
}
|
}
|
||||||
|
@ -481,7 +468,7 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
// Count triangles, discard lines and points
|
// Count triangles, discard lines and points
|
||||||
uint16_t count = 0;
|
uint16_t count = 0;
|
||||||
for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
|
for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
|
||||||
const aiFace& f = mesh.mFaces[i];
|
const aiFace &f = mesh.mFaces[i];
|
||||||
if (f.mNumIndices < 3) {
|
if (f.mNumIndices < 3) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -492,7 +479,7 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
|
|
||||||
writer.PutU2(count);
|
writer.PutU2(count);
|
||||||
for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
|
for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
|
||||||
const aiFace& f = mesh.mFaces[i];
|
const aiFace &f = mesh.mFaces[i];
|
||||||
if (f.mNumIndices < 3) {
|
if (f.mNumIndices < 3) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -524,10 +511,9 @@ void Discreet3DSExporter::WriteMeshes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
|
void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh &mesh) {
|
||||||
{
|
|
||||||
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
|
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
|
||||||
const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
|
const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
|
||||||
WriteString(name);
|
WriteString(name);
|
||||||
|
|
||||||
// Because assimp splits meshes by material, only a single
|
// Because assimp splits meshes by material, only a single
|
||||||
|
@ -542,7 +528,7 @@ void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteString(const std::string& s) {
|
void Discreet3DSExporter::WriteString(const std::string &s) {
|
||||||
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
|
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
|
||||||
writer.PutI1(*it);
|
writer.PutI1(*it);
|
||||||
}
|
}
|
||||||
|
@ -550,7 +536,7 @@ void Discreet3DSExporter::WriteString(const std::string& s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteString(const aiString& s) {
|
void Discreet3DSExporter::WriteString(const aiString &s) {
|
||||||
for (std::size_t i = 0; i < s.length; ++i) {
|
for (std::size_t i = 0; i < s.length; ++i) {
|
||||||
writer.PutI1(s.data[i]);
|
writer.PutI1(s.data[i]);
|
||||||
}
|
}
|
||||||
|
@ -558,7 +544,7 @@ void Discreet3DSExporter::WriteString(const aiString& s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
|
void Discreet3DSExporter::WriteColor(const aiColor3D &color) {
|
||||||
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
|
ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
|
||||||
writer.PutF4(color.r);
|
writer.PutF4(color.r);
|
||||||
writer.PutF4(color.g);
|
writer.PutF4(color.g);
|
||||||
|
@ -577,6 +563,5 @@ void Discreet3DSExporter::WritePercentChunk(double f) {
|
||||||
writer.PutF8(f);
|
writer.PutF8(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_3DS_EXPORTER
|
#endif // ASSIMP_BUILD_NO_3DS_EXPORTER
|
||||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
File diff suppressed because it is too large
Load Diff
|
@ -44,81 +44,74 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "D3MFExporter.h"
|
#include "D3MFExporter.h"
|
||||||
|
|
||||||
#include <assimp/scene.h>
|
|
||||||
#include <assimp/IOSystem.hpp>
|
|
||||||
#include <assimp/IOStream.hpp>
|
|
||||||
#include <assimp/Exporter.hpp>
|
|
||||||
#include <assimp/DefaultLogger.hpp>
|
|
||||||
#include <assimp/StringUtils.h>
|
|
||||||
#include <assimp/Exceptional.h>
|
#include <assimp/Exceptional.h>
|
||||||
|
#include <assimp/StringUtils.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
#include <assimp/Exporter.hpp>
|
||||||
|
#include <assimp/IOStream.hpp>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
#include "3MFXmlTags.h"
|
#include "3MFXmlTags.h"
|
||||||
#include "D3MFOpcPackage.h"
|
#include "D3MFOpcPackage.h"
|
||||||
|
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#ifdef ASSIMP_USE_HUNTER
|
||||||
# include <zip/zip.h>
|
#include <zip/zip.h>
|
||||||
#else
|
#else
|
||||||
# include <contrib/zip/src/zip.h>
|
#include <contrib/zip/src/zip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
|
void ExportScene3MF(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
|
||||||
if ( nullptr == pIOSystem ) {
|
if (nullptr == pIOSystem) {
|
||||||
throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
|
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||||||
}
|
}
|
||||||
D3MF::D3MFExporter myExporter( pFile, pScene );
|
D3MF::D3MFExporter myExporter(pFile, pScene);
|
||||||
if ( myExporter.validate() ) {
|
if (myExporter.validate()) {
|
||||||
if ( pIOSystem->Exists( pFile ) ) {
|
if (pIOSystem->Exists(pFile)) {
|
||||||
if ( !pIOSystem->DeleteFile( pFile ) ) {
|
if (!pIOSystem->DeleteFile(pFile)) {
|
||||||
throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
|
throw DeadlyExportError("File exists, cannot override : " + std::string(pFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool ok = myExporter.exportArchive(pFile);
|
bool ok = myExporter.exportArchive(pFile);
|
||||||
if ( !ok ) {
|
if (!ok) {
|
||||||
throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
|
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace D3MF {
|
namespace D3MF {
|
||||||
|
|
||||||
D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
|
D3MFExporter::D3MFExporter(const char *pFile, const aiScene *pScene) :
|
||||||
: mArchiveName( pFile )
|
mArchiveName(pFile), m_zipArchive(nullptr), mScene(pScene), mModelOutput(), mRelOutput(), mContentOutput(), mBuildItems(), mRelations() {
|
||||||
, m_zipArchive( nullptr )
|
|
||||||
, mScene( pScene )
|
|
||||||
, mModelOutput()
|
|
||||||
, mRelOutput()
|
|
||||||
, mContentOutput()
|
|
||||||
, mBuildItems()
|
|
||||||
, mRelations() {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
D3MFExporter::~D3MFExporter() {
|
D3MFExporter::~D3MFExporter() {
|
||||||
for ( size_t i = 0; i < mRelations.size(); ++i ) {
|
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||||||
delete mRelations[ i ];
|
delete mRelations[i];
|
||||||
}
|
}
|
||||||
mRelations.clear();
|
mRelations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3MFExporter::validate() {
|
bool D3MFExporter::validate() {
|
||||||
if ( mArchiveName.empty() ) {
|
if (mArchiveName.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( nullptr == mScene ) {
|
if (nullptr == mScene) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3MFExporter::exportArchive( const char *file ) {
|
bool D3MFExporter::exportArchive(const char *file) {
|
||||||
bool ok( true );
|
bool ok(true);
|
||||||
|
|
||||||
m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
|
m_zipArchive = zip_open(file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
|
||||||
if ( nullptr == m_zipArchive ) {
|
if (nullptr == m_zipArchive) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +119,7 @@ bool D3MFExporter::exportArchive( const char *file ) {
|
||||||
ok |= export3DModel();
|
ok |= export3DModel();
|
||||||
ok |= exportRelations();
|
ok |= exportRelations();
|
||||||
|
|
||||||
zip_close( m_zipArchive );
|
zip_close(m_zipArchive);
|
||||||
m_zipArchive = nullptr;
|
m_zipArchive = nullptr;
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
|
@ -145,7 +138,7 @@ bool D3MFExporter::exportContentTypes() {
|
||||||
mContentOutput << std::endl;
|
mContentOutput << std::endl;
|
||||||
mContentOutput << "</Types>";
|
mContentOutput << "</Types>";
|
||||||
mContentOutput << std::endl;
|
mContentOutput << std::endl;
|
||||||
exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
|
exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -157,20 +150,20 @@ bool D3MFExporter::exportRelations() {
|
||||||
mRelOutput << std::endl;
|
mRelOutput << std::endl;
|
||||||
mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
|
mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
|
||||||
|
|
||||||
for ( size_t i = 0; i < mRelations.size(); ++i ) {
|
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||||||
if ( mRelations[ i ]->target[ 0 ] == '/' ) {
|
if (mRelations[i]->target[0] == '/') {
|
||||||
mRelOutput << "<Relationship Target=\"" << mRelations[ i ]->target << "\" ";
|
mRelOutput << "<Relationship Target=\"" << mRelations[i]->target << "\" ";
|
||||||
} else {
|
} else {
|
||||||
mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
|
mRelOutput << "<Relationship Target=\"/" << mRelations[i]->target << "\" ";
|
||||||
}
|
}
|
||||||
mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
|
mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
|
||||||
mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
|
mRelOutput << "Type=\"" << mRelations[i]->type << "\" />";
|
||||||
mRelOutput << std::endl;
|
mRelOutput << std::endl;
|
||||||
}
|
}
|
||||||
mRelOutput << "</Relationships>";
|
mRelOutput << "</Relationships>";
|
||||||
mRelOutput << std::endl;
|
mRelOutput << std::endl;
|
||||||
|
|
||||||
writeRelInfoToFile( "_rels", ".rels" );
|
writeRelInfoToFile("_rels", ".rels");
|
||||||
mRelOutput.flush();
|
mRelOutput.flush();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -181,8 +174,8 @@ bool D3MFExporter::export3DModel() {
|
||||||
|
|
||||||
writeHeader();
|
writeHeader();
|
||||||
mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
|
mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
|
||||||
<< " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
|
<< " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
mModelOutput << "<" << XmlTag::resources << ">";
|
mModelOutput << "<" << XmlTag::resources << ">";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
|
|
||||||
|
@ -192,7 +185,6 @@ bool D3MFExporter::export3DModel() {
|
||||||
|
|
||||||
writeObjects();
|
writeObjects();
|
||||||
|
|
||||||
|
|
||||||
mModelOutput << "</" << XmlTag::resources << ">";
|
mModelOutput << "</" << XmlTag::resources << ">";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
writeBuild();
|
writeBuild();
|
||||||
|
@ -203,9 +195,9 @@ bool D3MFExporter::export3DModel() {
|
||||||
info->id = "rel0";
|
info->id = "rel0";
|
||||||
info->target = "/3D/3DModel.model";
|
info->target = "/3D/3DModel.model";
|
||||||
info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
|
info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
|
||||||
mRelations.push_back( info );
|
mRelations.push_back(info);
|
||||||
|
|
||||||
writeModelToArchive( "3D", "3DModel.model" );
|
writeModelToArchive("3D", "3DModel.model");
|
||||||
mModelOutput.flush();
|
mModelOutput.flush();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -217,22 +209,22 @@ void D3MFExporter::writeHeader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeMetaData() {
|
void D3MFExporter::writeMetaData() {
|
||||||
if ( nullptr == mScene->mMetaData ) {
|
if (nullptr == mScene->mMetaData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
|
const unsigned int numMetaEntries(mScene->mMetaData->mNumProperties);
|
||||||
if ( 0 == numMetaEntries ) {
|
if (0 == numMetaEntries) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aiString *key = nullptr;
|
const aiString *key = nullptr;
|
||||||
const aiMetadataEntry *entry(nullptr);
|
const aiMetadataEntry *entry(nullptr);
|
||||||
for ( size_t i = 0; i < numMetaEntries; ++i ) {
|
for (size_t i = 0; i < numMetaEntries; ++i) {
|
||||||
mScene->mMetaData->Get( i, key, entry );
|
mScene->mMetaData->Get(i, key, entry);
|
||||||
std::string k( key->C_Str() );
|
std::string k(key->C_Str());
|
||||||
aiString value;
|
aiString value;
|
||||||
mScene->mMetaData->Get( k, value );
|
mScene->mMetaData->Get(k, value);
|
||||||
mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
|
mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
|
||||||
mModelOutput << value.C_Str();
|
mModelOutput << value.C_Str();
|
||||||
mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
|
mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
|
||||||
|
@ -241,115 +233,114 @@ void D3MFExporter::writeMetaData() {
|
||||||
|
|
||||||
void D3MFExporter::writeBaseMaterials() {
|
void D3MFExporter::writeBaseMaterials() {
|
||||||
mModelOutput << "<basematerials id=\"1\">\n";
|
mModelOutput << "<basematerials id=\"1\">\n";
|
||||||
std::string strName, hexDiffuseColor , tmp;
|
std::string strName, hexDiffuseColor, tmp;
|
||||||
for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {
|
for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
|
||||||
aiMaterial *mat = mScene->mMaterials[ i ];
|
aiMaterial *mat = mScene->mMaterials[i];
|
||||||
aiString name;
|
aiString name;
|
||||||
if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) {
|
if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
|
||||||
strName = "basemat_" + to_string( i );
|
strName = "basemat_" + to_string(i);
|
||||||
} else {
|
} else {
|
||||||
strName = name.C_Str();
|
strName = name.C_Str();
|
||||||
}
|
}
|
||||||
aiColor4D color;
|
aiColor4D color;
|
||||||
if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) {
|
if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) {
|
||||||
hexDiffuseColor.clear();
|
hexDiffuseColor.clear();
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
// rgbs %
|
// rgbs %
|
||||||
if(color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1){
|
if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
|
||||||
|
|
||||||
hexDiffuseColor = Rgba2Hex(
|
hexDiffuseColor = Rgba2Hex(
|
||||||
(int)((ai_real)color.r)*255,
|
(int)((ai_real)color.r) * 255,
|
||||||
(int)((ai_real)color.g)*255,
|
(int)((ai_real)color.g) * 255,
|
||||||
(int)((ai_real)color.b)*255,
|
(int)((ai_real)color.b) * 255,
|
||||||
(int)((ai_real)color.a)*255,
|
(int)((ai_real)color.a) * 255,
|
||||||
true
|
true);
|
||||||
);
|
|
||||||
|
} else {
|
||||||
}else{
|
hexDiffuseColor = "#";
|
||||||
hexDiffuseColor = "#";
|
tmp = DecimalToHexa((ai_real)color.r);
|
||||||
tmp = DecimalToHexa( (ai_real) color.r );
|
hexDiffuseColor += tmp;
|
||||||
hexDiffuseColor += tmp;
|
tmp = DecimalToHexa((ai_real)color.g);
|
||||||
tmp = DecimalToHexa((ai_real)color.g);
|
hexDiffuseColor += tmp;
|
||||||
hexDiffuseColor += tmp;
|
tmp = DecimalToHexa((ai_real)color.b);
|
||||||
tmp = DecimalToHexa((ai_real)color.b);
|
hexDiffuseColor += tmp;
|
||||||
hexDiffuseColor += tmp;
|
tmp = DecimalToHexa((ai_real)color.a);
|
||||||
tmp = DecimalToHexa((ai_real)color.a);
|
hexDiffuseColor += tmp;
|
||||||
hexDiffuseColor += tmp;
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
hexDiffuseColor = "#FFFFFFFF";
|
hexDiffuseColor = "#FFFFFFFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
mModelOutput << "<base name=\""+strName+"\" "+" displaycolor=\""+hexDiffuseColor+"\" />\n";
|
mModelOutput << "<base name=\"" + strName + "\" " + " displaycolor=\"" + hexDiffuseColor + "\" />\n";
|
||||||
}
|
}
|
||||||
mModelOutput << "</basematerials>\n";
|
mModelOutput << "</basematerials>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeObjects() {
|
void D3MFExporter::writeObjects() {
|
||||||
if ( nullptr == mScene->mRootNode ) {
|
if (nullptr == mScene->mRootNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aiNode *root = mScene->mRootNode;
|
aiNode *root = mScene->mRootNode;
|
||||||
for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
|
for (unsigned int i = 0; i < root->mNumChildren; ++i) {
|
||||||
aiNode *currentNode( root->mChildren[ i ] );
|
aiNode *currentNode(root->mChildren[i]);
|
||||||
if ( nullptr == currentNode ) {
|
if (nullptr == currentNode) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
|
mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
|
for (unsigned int j = 0; j < currentNode->mNumMeshes; ++j) {
|
||||||
aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
|
aiMesh *currentMesh = mScene->mMeshes[currentNode->mMeshes[j]];
|
||||||
if ( nullptr == currentMesh ) {
|
if (nullptr == currentMesh) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writeMesh( currentMesh );
|
writeMesh(currentMesh);
|
||||||
}
|
}
|
||||||
mBuildItems.push_back( i );
|
mBuildItems.push_back(i);
|
||||||
|
|
||||||
mModelOutput << "</" << XmlTag::object << ">";
|
mModelOutput << "</" << XmlTag::object << ">";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeMesh( aiMesh *mesh ) {
|
void D3MFExporter::writeMesh(aiMesh *mesh) {
|
||||||
if ( nullptr == mesh ) {
|
if (nullptr == mesh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
|
mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
|
||||||
mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
|
mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
|
||||||
for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
|
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||||||
writeVertex( mesh->mVertices[ i ] );
|
writeVertex(mesh->mVertices[i]);
|
||||||
}
|
}
|
||||||
mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
|
mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
|
||||||
|
|
||||||
const unsigned int matIdx( mesh->mMaterialIndex );
|
const unsigned int matIdx(mesh->mMaterialIndex);
|
||||||
|
|
||||||
writeFaces( mesh, matIdx );
|
writeFaces(mesh, matIdx);
|
||||||
|
|
||||||
mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
|
mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeVertex( const aiVector3D &pos ) {
|
void D3MFExporter::writeVertex(const aiVector3D &pos) {
|
||||||
mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
|
mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
|
void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) {
|
||||||
if ( nullptr == mesh ) {
|
if (nullptr == mesh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !mesh->HasFaces() ) {
|
if (!mesh->HasFaces()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
|
mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
|
||||||
for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
|
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
|
||||||
aiFace ¤tFace = mesh->mFaces[ i ];
|
aiFace ¤tFace = mesh->mFaces[i];
|
||||||
mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
|
mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\""
|
||||||
<< currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ]
|
<< currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2]
|
||||||
<< "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />";
|
<< "\" pid=\"1\" p1=\"" + to_string(matIdx) + "\" />";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
}
|
}
|
||||||
mModelOutput << "</" << XmlTag::triangles << ">";
|
mModelOutput << "</" << XmlTag::triangles << ">";
|
||||||
|
@ -359,7 +350,7 @@ void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
|
||||||
void D3MFExporter::writeBuild() {
|
void D3MFExporter::writeBuild() {
|
||||||
mModelOutput << "<" << XmlTag::build << ">" << std::endl;
|
mModelOutput << "<" << XmlTag::build << ">" << std::endl;
|
||||||
|
|
||||||
for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
|
for (size_t i = 0; i < mBuildItems.size(); ++i) {
|
||||||
mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
|
mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -367,46 +358,45 @@ void D3MFExporter::writeBuild() {
|
||||||
mModelOutput << std::endl;
|
mModelOutput << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::exportContentTyp( const std::string &filename ) {
|
void D3MFExporter::exportContentTyp(const std::string &filename) {
|
||||||
if ( nullptr == m_zipArchive ) {
|
if (nullptr == m_zipArchive) {
|
||||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||||
}
|
}
|
||||||
const std::string entry = filename;
|
const std::string entry = filename;
|
||||||
zip_entry_open( m_zipArchive, entry.c_str() );
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||||||
|
|
||||||
const std::string &exportTxt( mContentOutput.str() );
|
const std::string &exportTxt(mContentOutput.str());
|
||||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||||||
|
|
||||||
zip_entry_close( m_zipArchive );
|
zip_entry_close(m_zipArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
|
void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) {
|
||||||
if ( nullptr == m_zipArchive ) {
|
if (nullptr == m_zipArchive) {
|
||||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||||
}
|
}
|
||||||
const std::string entry = folder + "/" + modelName;
|
const std::string entry = folder + "/" + modelName;
|
||||||
zip_entry_open( m_zipArchive, entry.c_str() );
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||||||
|
|
||||||
const std::string &exportTxt( mModelOutput.str() );
|
const std::string &exportTxt(mModelOutput.str());
|
||||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||||||
|
|
||||||
zip_entry_close( m_zipArchive );
|
zip_entry_close(m_zipArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
|
void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) {
|
||||||
if ( nullptr == m_zipArchive ) {
|
if (nullptr == m_zipArchive) {
|
||||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||||
}
|
}
|
||||||
const std::string entry = folder + "/" + relName;
|
const std::string entry = folder + "/" + relName;
|
||||||
zip_entry_open( m_zipArchive, entry.c_str() );
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||||||
|
|
||||||
const std::string &exportTxt( mRelOutput.str() );
|
const std::string &exportTxt(mRelOutput.str());
|
||||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||||||
|
|
||||||
zip_entry_close( m_zipArchive );
|
zip_entry_close(m_zipArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // Namespace D3MF
|
} // Namespace D3MF
|
||||||
} // Namespace Assimp
|
} // Namespace Assimp
|
||||||
|
|
|
@ -44,24 +44,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "D3MFImporter.h"
|
#include "D3MFImporter.h"
|
||||||
|
|
||||||
#include <assimp/scene.h>
|
|
||||||
#include <assimp/IOSystem.hpp>
|
|
||||||
#include <assimp/DefaultLogger.hpp>
|
|
||||||
#include <assimp/importerdesc.h>
|
|
||||||
#include <assimp/StringComparison.h>
|
#include <assimp/StringComparison.h>
|
||||||
#include <assimp/StringUtils.h>
|
#include <assimp/StringUtils.h>
|
||||||
#include <assimp/ZipArchiveIOSystem.h>
|
#include <assimp/ZipArchiveIOSystem.h>
|
||||||
|
#include <assimp/importerdesc.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "D3MFOpcPackage.h"
|
|
||||||
#include <assimp/irrXMLWrapper.h>
|
|
||||||
#include "3MFXmlTags.h"
|
#include "3MFXmlTags.h"
|
||||||
|
#include "D3MFOpcPackage.h"
|
||||||
#include <assimp/fast_atof.h>
|
#include <assimp/fast_atof.h>
|
||||||
|
#include <assimp/irrXMLWrapper.h>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
|
@ -70,90 +70,90 @@ namespace D3MF {
|
||||||
|
|
||||||
class XmlSerializer {
|
class XmlSerializer {
|
||||||
public:
|
public:
|
||||||
using MatArray = std::vector<aiMaterial*>;
|
using MatArray = std::vector<aiMaterial *>;
|
||||||
using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
|
using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
|
||||||
|
|
||||||
XmlSerializer(XmlReader* xmlReader)
|
XmlSerializer(XmlReader *xmlReader) :
|
||||||
: mMeshes()
|
mMeshes(),
|
||||||
, mMatArray()
|
mMatArray(),
|
||||||
, mActiveMatGroup( 99999999 )
|
mActiveMatGroup(99999999),
|
||||||
, mMatId2MatArray()
|
mMatId2MatArray(),
|
||||||
, xmlReader(xmlReader){
|
xmlReader(xmlReader) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
~XmlSerializer() {
|
~XmlSerializer() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportXml(aiScene* scene) {
|
void ImportXml(aiScene *scene) {
|
||||||
if ( nullptr == scene ) {
|
if (nullptr == scene) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene->mRootNode = new aiNode();
|
scene->mRootNode = new aiNode();
|
||||||
std::vector<aiNode*> children;
|
std::vector<aiNode *> children;
|
||||||
|
|
||||||
std::string nodeName;
|
std::string nodeName;
|
||||||
while(ReadToEndElement(D3MF::XmlTag::model)) {
|
while (ReadToEndElement(D3MF::XmlTag::model)) {
|
||||||
nodeName = xmlReader->getNodeName();
|
nodeName = xmlReader->getNodeName();
|
||||||
if( nodeName == D3MF::XmlTag::object) {
|
if (nodeName == D3MF::XmlTag::object) {
|
||||||
children.push_back(ReadObject(scene));
|
children.push_back(ReadObject(scene));
|
||||||
} else if( nodeName == D3MF::XmlTag::build) {
|
} else if (nodeName == D3MF::XmlTag::build) {
|
||||||
//
|
//
|
||||||
} else if ( nodeName == D3MF::XmlTag::basematerials ) {
|
} else if (nodeName == D3MF::XmlTag::basematerials) {
|
||||||
ReadBaseMaterials();
|
ReadBaseMaterials();
|
||||||
} else if ( nodeName == D3MF::XmlTag::meta ) {
|
} else if (nodeName == D3MF::XmlTag::meta) {
|
||||||
ReadMetadata();
|
ReadMetadata();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( scene->mRootNode->mName.length == 0 ) {
|
if (scene->mRootNode->mName.length == 0) {
|
||||||
scene->mRootNode->mName.Set( "3MF" );
|
scene->mRootNode->mName.Set("3MF");
|
||||||
}
|
}
|
||||||
|
|
||||||
// import the metadata
|
// import the metadata
|
||||||
if ( !mMetaData.empty() ) {
|
if (!mMetaData.empty()) {
|
||||||
const size_t numMeta( mMetaData.size() );
|
const size_t numMeta(mMetaData.size());
|
||||||
scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>( numMeta ) );
|
scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
|
||||||
for ( size_t i = 0; i < numMeta; ++i ) {
|
for (size_t i = 0; i < numMeta; ++i) {
|
||||||
aiString val( mMetaData[ i ].value );
|
aiString val(mMetaData[i].value);
|
||||||
scene->mMetaData->Set(static_cast<unsigned int>( i ), mMetaData[ i ].name, val );
|
scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// import the meshes
|
// import the meshes
|
||||||
scene->mNumMeshes = static_cast<unsigned int>( mMeshes.size());
|
scene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
|
||||||
scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
|
scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
|
||||||
std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes);
|
std::copy(mMeshes.begin(), mMeshes.end(), scene->mMeshes);
|
||||||
|
|
||||||
// import the materials
|
// import the materials
|
||||||
scene->mNumMaterials = static_cast<unsigned int>( mMatArray.size() );
|
scene->mNumMaterials = static_cast<unsigned int>(mMatArray.size());
|
||||||
if ( 0 != scene->mNumMaterials ) {
|
if (0 != scene->mNumMaterials) {
|
||||||
scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ];
|
scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
|
||||||
std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials );
|
std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the scenegraph
|
// create the scenegraph
|
||||||
scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
|
scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
|
||||||
scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
|
scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren]();
|
||||||
std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
|
std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
aiNode* ReadObject(aiScene* scene) {
|
aiNode *ReadObject(aiScene *scene) {
|
||||||
std::unique_ptr<aiNode> node(new aiNode());
|
std::unique_ptr<aiNode> node(new aiNode());
|
||||||
|
|
||||||
std::vector<unsigned long> meshIds;
|
std::vector<unsigned long> meshIds;
|
||||||
|
|
||||||
const char *attrib( nullptr );
|
const char *attrib(nullptr);
|
||||||
std::string name, type;
|
std::string name, type;
|
||||||
attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() );
|
attrib = xmlReader->getAttributeValue(D3MF::XmlTag::id.c_str());
|
||||||
if ( nullptr != attrib ) {
|
if (nullptr != attrib) {
|
||||||
name = attrib;
|
name = attrib;
|
||||||
}
|
}
|
||||||
attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() );
|
attrib = xmlReader->getAttributeValue(D3MF::XmlTag::type.c_str());
|
||||||
if ( nullptr != attrib ) {
|
if (nullptr != attrib) {
|
||||||
type = attrib;
|
type = attrib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,8 +162,8 @@ private:
|
||||||
|
|
||||||
size_t meshIdx = mMeshes.size();
|
size_t meshIdx = mMeshes.size();
|
||||||
|
|
||||||
while(ReadToEndElement(D3MF::XmlTag::object)) {
|
while (ReadToEndElement(D3MF::XmlTag::object)) {
|
||||||
if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
|
if (xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
|
||||||
auto mesh = ReadMesh();
|
auto mesh = ReadMesh();
|
||||||
|
|
||||||
mesh->mName.Set(name);
|
mesh->mName.Set(name);
|
||||||
|
@ -183,11 +183,11 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMesh *ReadMesh() {
|
aiMesh *ReadMesh() {
|
||||||
aiMesh* mesh = new aiMesh();
|
aiMesh *mesh = new aiMesh();
|
||||||
while(ReadToEndElement(D3MF::XmlTag::mesh)) {
|
while (ReadToEndElement(D3MF::XmlTag::mesh)) {
|
||||||
if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
|
if (xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
|
||||||
ImportVertices(mesh);
|
ImportVertices(mesh);
|
||||||
} else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
|
} else if (xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
|
||||||
ImportTriangles(mesh);
|
ImportTriangles(mesh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,24 +196,24 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadMetadata() {
|
void ReadMetadata() {
|
||||||
const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() );
|
const std::string name = xmlReader->getAttributeValue(D3MF::XmlTag::meta_name.c_str());
|
||||||
xmlReader->read();
|
xmlReader->read();
|
||||||
const std::string value = xmlReader->getNodeData();
|
const std::string value = xmlReader->getNodeData();
|
||||||
|
|
||||||
if ( name.empty() ) {
|
if (name.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaEntry entry;
|
MetaEntry entry;
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
entry.value = value;
|
entry.value = value;
|
||||||
mMetaData.push_back( entry );
|
mMetaData.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportVertices(aiMesh* mesh) {
|
void ImportVertices(aiMesh *mesh) {
|
||||||
std::vector<aiVector3D> vertices;
|
std::vector<aiVector3D> vertices;
|
||||||
while(ReadToEndElement(D3MF::XmlTag::vertices)) {
|
while (ReadToEndElement(D3MF::XmlTag::vertices)) {
|
||||||
if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
|
if (xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
|
||||||
vertices.push_back(ReadVertex());
|
vertices.push_back(ReadVertex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,20 +233,20 @@ private:
|
||||||
return vertex;
|
return vertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportTriangles(aiMesh* mesh) {
|
void ImportTriangles(aiMesh *mesh) {
|
||||||
std::vector<aiFace> faces;
|
std::vector<aiFace> faces;
|
||||||
|
|
||||||
while(ReadToEndElement(D3MF::XmlTag::triangles)) {
|
while (ReadToEndElement(D3MF::XmlTag::triangles)) {
|
||||||
const std::string nodeName( xmlReader->getNodeName() );
|
const std::string nodeName(xmlReader->getNodeName());
|
||||||
if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
|
if (xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
|
||||||
faces.push_back(ReadTriangle());
|
faces.push_back(ReadTriangle());
|
||||||
const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) );
|
const char *pidToken(xmlReader->getAttributeValue(D3MF::XmlTag::p1.c_str()));
|
||||||
if ( nullptr != pidToken ) {
|
if (nullptr != pidToken) {
|
||||||
int matIdx( std::atoi( pidToken ) );
|
int matIdx(std::atoi(pidToken));
|
||||||
mesh->mMaterialIndex = matIdx;
|
mesh->mMaterialIndex = matIdx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->mNumFaces = static_cast<unsigned int>(faces.size());
|
mesh->mNumFaces = static_cast<unsigned int>(faces.size());
|
||||||
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
||||||
|
@ -269,117 +269,115 @@ private:
|
||||||
|
|
||||||
void ReadBaseMaterials() {
|
void ReadBaseMaterials() {
|
||||||
std::vector<unsigned int> MatIdArray;
|
std::vector<unsigned int> MatIdArray;
|
||||||
const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) );
|
const char *baseMaterialId(xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_id.c_str()));
|
||||||
if ( nullptr != baseMaterialId ) {
|
if (nullptr != baseMaterialId) {
|
||||||
unsigned int id = std::atoi( baseMaterialId );
|
unsigned int id = std::atoi(baseMaterialId);
|
||||||
const size_t newMatIdx( mMatArray.size() );
|
const size_t newMatIdx(mMatArray.size());
|
||||||
if ( id != mActiveMatGroup ) {
|
if (id != mActiveMatGroup) {
|
||||||
mActiveMatGroup = id;
|
mActiveMatGroup = id;
|
||||||
MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) );
|
MatId2MatArray::const_iterator it(mMatId2MatArray.find(id));
|
||||||
if ( mMatId2MatArray.end() == it ) {
|
if (mMatId2MatArray.end() == it) {
|
||||||
MatIdArray.clear();
|
MatIdArray.clear();
|
||||||
mMatId2MatArray[ id ] = MatIdArray;
|
mMatId2MatArray[id] = MatIdArray;
|
||||||
} else {
|
} else {
|
||||||
MatIdArray = it->second;
|
MatIdArray = it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MatIdArray.push_back( static_cast<unsigned int>( newMatIdx ) );
|
MatIdArray.push_back(static_cast<unsigned int>(newMatIdx));
|
||||||
mMatId2MatArray[ mActiveMatGroup ] = MatIdArray;
|
mMatId2MatArray[mActiveMatGroup] = MatIdArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) {
|
while (ReadToEndElement(D3MF::XmlTag::basematerials)) {
|
||||||
mMatArray.push_back( readMaterialDef() );
|
mMatArray.push_back(readMaterialDef());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseColor( const char *color, aiColor4D &diffuse ) {
|
bool parseColor(const char *color, aiColor4D &diffuse) {
|
||||||
if ( nullptr == color ) {
|
if (nullptr == color) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
|
//format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
|
||||||
const size_t len( strlen( color ) );
|
const size_t len(strlen(color));
|
||||||
if ( 9 != len && 7 != len) {
|
if (9 != len && 7 != len) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *buf( color );
|
const char *buf(color);
|
||||||
if ( '#' != *buf ) {
|
if ('#' != *buf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
++buf;
|
++buf;
|
||||||
char comp[ 3 ] = { 0,0,'\0' };
|
char comp[3] = { 0, 0, '\0' };
|
||||||
|
|
||||||
comp[ 0 ] = *buf;
|
comp[0] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
comp[ 1 ] = *buf;
|
comp[1] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
|
diffuse.r = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
|
||||||
|
|
||||||
|
comp[0] = *buf;
|
||||||
|
++buf;
|
||||||
|
comp[1] = *buf;
|
||||||
|
++buf;
|
||||||
|
diffuse.g = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
|
||||||
|
|
||||||
comp[ 0 ] = *buf;
|
comp[0] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
comp[ 1 ] = *buf;
|
comp[1] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
|
diffuse.b = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
|
||||||
|
|
||||||
comp[ 0 ] = *buf;
|
if (7 == len)
|
||||||
++buf;
|
|
||||||
comp[ 1 ] = *buf;
|
|
||||||
++buf;
|
|
||||||
diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
|
|
||||||
|
|
||||||
if(7 == len)
|
|
||||||
return true;
|
return true;
|
||||||
comp[ 0 ] = *buf;
|
comp[0] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
comp[ 1 ] = *buf;
|
comp[1] = *buf;
|
||||||
++buf;
|
++buf;
|
||||||
diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
|
diffuse.a = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assignDiffuseColor( aiMaterial *mat ) {
|
void assignDiffuseColor(aiMaterial *mat) {
|
||||||
const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() );
|
const char *color = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_displaycolor.c_str());
|
||||||
aiColor4D diffuse;
|
aiColor4D diffuse;
|
||||||
if ( parseColor( color, diffuse ) ) {
|
if (parseColor(color, diffuse)) {
|
||||||
mat->AddProperty<aiColor4D>( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
|
mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
aiMaterial *readMaterialDef() {
|
aiMaterial *readMaterialDef() {
|
||||||
aiMaterial *mat( nullptr );
|
aiMaterial *mat(nullptr);
|
||||||
const char *name( nullptr );
|
const char *name(nullptr);
|
||||||
const std::string nodeName( xmlReader->getNodeName() );
|
const std::string nodeName(xmlReader->getNodeName());
|
||||||
if ( nodeName == D3MF::XmlTag::basematerials_base ) {
|
if (nodeName == D3MF::XmlTag::basematerials_base) {
|
||||||
name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() );
|
name = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_name.c_str());
|
||||||
std::string stdMatName;
|
std::string stdMatName;
|
||||||
aiString matName;
|
aiString matName;
|
||||||
std::string strId( to_string( mActiveMatGroup ) );
|
std::string strId(to_string(mActiveMatGroup));
|
||||||
stdMatName += "id";
|
stdMatName += "id";
|
||||||
stdMatName += strId;
|
stdMatName += strId;
|
||||||
stdMatName += "_";
|
stdMatName += "_";
|
||||||
if ( nullptr != name ) {
|
if (nullptr != name) {
|
||||||
stdMatName += std::string( name );
|
stdMatName += std::string(name);
|
||||||
} else {
|
} else {
|
||||||
stdMatName += "basemat";
|
stdMatName += "basemat";
|
||||||
}
|
}
|
||||||
matName.Set( stdMatName );
|
matName.Set(stdMatName);
|
||||||
|
|
||||||
mat = new aiMaterial;
|
mat = new aiMaterial;
|
||||||
mat->AddProperty( &matName, AI_MATKEY_NAME );
|
mat->AddProperty(&matName, AI_MATKEY_NAME);
|
||||||
|
|
||||||
assignDiffuseColor( mat );
|
assignDiffuseColor(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool ReadToStartElement(const std::string& startTag) {
|
bool ReadToStartElement(const std::string &startTag) {
|
||||||
while(xmlReader->read()) {
|
while (xmlReader->read()) {
|
||||||
const std::string &nodeName( xmlReader->getNodeName() );
|
const std::string &nodeName(xmlReader->getNodeName());
|
||||||
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) {
|
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) {
|
||||||
return true;
|
return true;
|
||||||
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) {
|
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) {
|
||||||
|
@ -390,9 +388,9 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadToEndElement(const std::string& closeTag) {
|
bool ReadToEndElement(const std::string &closeTag) {
|
||||||
while(xmlReader->read()) {
|
while (xmlReader->read()) {
|
||||||
const std::string &nodeName( xmlReader->getNodeName() );
|
const std::string &nodeName(xmlReader->getNodeName());
|
||||||
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
|
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
|
||||||
return true;
|
return true;
|
||||||
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) {
|
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) {
|
||||||
|
@ -410,11 +408,11 @@ private:
|
||||||
std::string value;
|
std::string value;
|
||||||
};
|
};
|
||||||
std::vector<MetaEntry> mMetaData;
|
std::vector<MetaEntry> mMetaData;
|
||||||
std::vector<aiMesh*> mMeshes;
|
std::vector<aiMesh *> mMeshes;
|
||||||
MatArray mMatArray;
|
MatArray mMatArray;
|
||||||
unsigned int mActiveMatGroup;
|
unsigned int mActiveMatGroup;
|
||||||
MatId2MatArray mMatId2MatArray;
|
MatId2MatArray mMatId2MatArray;
|
||||||
XmlReader* xmlReader;
|
XmlReader *xmlReader;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace D3MF
|
} //namespace D3MF
|
||||||
|
@ -432,8 +430,8 @@ static const aiImporterDesc desc = {
|
||||||
"3mf"
|
"3mf"
|
||||||
};
|
};
|
||||||
|
|
||||||
D3MFImporter::D3MFImporter()
|
D3MFImporter::D3MFImporter() :
|
||||||
: BaseImporter() {
|
BaseImporter() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,17 +440,17 @@ D3MFImporter::~D3MFImporter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
|
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
|
||||||
const std::string extension( GetExtension( filename ) );
|
const std::string extension(GetExtension(filename));
|
||||||
if(extension == desc.mFileExtensions ) {
|
if (extension == desc.mFileExtensions) {
|
||||||
return true;
|
return true;
|
||||||
} else if ( !extension.length() || checkSig ) {
|
} else if (!extension.length() || checkSig) {
|
||||||
if ( nullptr == pIOHandler ) {
|
if (nullptr == pIOHandler) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
|
if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );
|
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
|
||||||
return opcPackage.validate();
|
return opcPackage.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +465,7 @@ const aiImporterDesc *D3MFImporter::GetInfo() const {
|
||||||
return &desc;
|
return &desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) {
|
void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
|
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
|
||||||
|
|
||||||
std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));
|
std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));
|
|
@ -192,7 +192,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
|
||||||
objects.push_back(Object());
|
objects.push_back(Object());
|
||||||
Object &obj = objects.back();
|
Object &obj = objects.back();
|
||||||
|
|
||||||
aiLight *light = NULL;
|
aiLight *light = nullptr;
|
||||||
if (!ASSIMP_strincmp(buffer, "light", 5)) {
|
if (!ASSIMP_strincmp(buffer, "light", 5)) {
|
||||||
// This is a light source. Add it to the list
|
// This is a light source. Add it to the list
|
||||||
mLights->push_back(light = new aiLight());
|
mLights->push_back(light = new aiLight());
|
||||||
|
@ -472,29 +472,29 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ((*it).flags & 0xf) {
|
switch ((*it).flags & 0xf) {
|
||||||
// closed line
|
// closed line
|
||||||
case 0x1:
|
case 0x1:
|
||||||
needMat[idx].first += (unsigned int)(*it).entries.size();
|
needMat[idx].first += (unsigned int)(*it).entries.size();
|
||||||
needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
|
needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// unclosed line
|
// unclosed line
|
||||||
case 0x2:
|
case 0x2:
|
||||||
needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
|
needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
|
||||||
needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
|
needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 0 == polygon, else unknown
|
// 0 == polygon, else unknown
|
||||||
default:
|
default:
|
||||||
if ((*it).flags & 0xf) {
|
if ((*it).flags & 0xf) {
|
||||||
ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
|
ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
|
||||||
(*it).flags &= ~(0xf);
|
(*it).flags &= ~(0xf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the number of faces increments by one, the number
|
// the number of faces increments by one, the number
|
||||||
// of vertices by surface.numref.
|
// of vertices by surface.numref.
|
||||||
needMat[idx].first++;
|
needMat[idx].first++;
|
||||||
needMat[idx].second += (unsigned int)(*it).entries.size();
|
needMat[idx].second += (unsigned int)(*it).entries.size();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
|
unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
|
||||||
|
@ -535,7 +535,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
|
||||||
|
|
||||||
// allocate UV coordinates, but only if the texture name for the
|
// allocate UV coordinates, but only if the texture name for the
|
||||||
// surface is not empty
|
// surface is not empty
|
||||||
aiVector3D *uv = NULL;
|
aiVector3D *uv = nullptr;
|
||||||
if (object.texture.length()) {
|
if (object.texture.length()) {
|
||||||
uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
|
uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
|
||||||
mesh->mNumUVComponents[0] = 2;
|
mesh->mNumUVComponents[0] = 2;
|
||||||
|
@ -644,20 +644,20 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
|
||||||
else {
|
else {
|
||||||
// generate a name depending on the type of the node
|
// generate a name depending on the type of the node
|
||||||
switch (object.type) {
|
switch (object.type) {
|
||||||
case Object::Group:
|
case Object::Group:
|
||||||
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
|
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
|
||||||
break;
|
break;
|
||||||
case Object::Poly:
|
case Object::Poly:
|
||||||
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
|
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
|
||||||
break;
|
break;
|
||||||
case Object::Light:
|
case Object::Light:
|
||||||
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
|
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// there shouldn't be more than one world, but we don't care
|
// there shouldn't be more than one world, but we don't care
|
||||||
case Object::World:
|
case Object::World:
|
||||||
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
|
node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
|
||||||
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// Check whether we can read from the file
|
||||||
if ( file.get() == nullptr ) {
|
if (file.get() == nullptr) {
|
||||||
throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
|
throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,27 +56,20 @@ struct aiMesh;
|
||||||
struct aiMaterial;
|
struct aiMaterial;
|
||||||
struct aiLight;
|
struct aiLight;
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
namespace Assimp {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** AC3D (*.ac) importer class
|
/** AC3D (*.ac) importer class
|
||||||
*/
|
*/
|
||||||
class AC3DImporter : public BaseImporter
|
class AC3DImporter : public BaseImporter {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
AC3DImporter();
|
AC3DImporter();
|
||||||
~AC3DImporter();
|
~AC3DImporter();
|
||||||
|
|
||||||
// Represents an AC3D material
|
// Represents an AC3D material
|
||||||
struct Material
|
struct Material {
|
||||||
{
|
Material() :
|
||||||
Material()
|
rgb(0.6f, 0.6f, 0.6f), spec(1.f, 1.f, 1.f), shin(0.f), trans(0.f) {}
|
||||||
: rgb (0.6f,0.6f,0.6f)
|
|
||||||
, spec (1.f,1.f,1.f)
|
|
||||||
, shin (0.f)
|
|
||||||
, trans (0.f)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// base color of the material
|
// base color of the material
|
||||||
aiColor3D rgb;
|
aiColor3D rgb;
|
||||||
|
@ -101,43 +94,25 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents an AC3D surface
|
// Represents an AC3D surface
|
||||||
struct Surface
|
struct Surface {
|
||||||
{
|
Surface() :
|
||||||
Surface()
|
mat(0), flags(0) {}
|
||||||
: mat (0)
|
|
||||||
, flags (0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
unsigned int mat,flags;
|
unsigned int mat, flags;
|
||||||
|
|
||||||
typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
|
typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
|
||||||
std::vector< SurfaceEntry > entries;
|
std::vector<SurfaceEntry> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents an AC3D object
|
// Represents an AC3D object
|
||||||
struct Object
|
struct Object {
|
||||||
{
|
Object() :
|
||||||
Object()
|
type(World), name(""), children(), texture(""), texRepeat(1.f, 1.f), texOffset(0.0f, 0.0f), rotation(), translation(), vertices(), surfaces(), numRefs(0), subDiv(0), crease() {}
|
||||||
: type (World)
|
|
||||||
, name( "" )
|
|
||||||
, children()
|
|
||||||
, texture( "" )
|
|
||||||
, texRepeat( 1.f, 1.f )
|
|
||||||
, texOffset( 0.0f, 0.0f )
|
|
||||||
, rotation()
|
|
||||||
, translation()
|
|
||||||
, vertices()
|
|
||||||
, surfaces()
|
|
||||||
, numRefs (0)
|
|
||||||
, subDiv (0)
|
|
||||||
, crease()
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Type description
|
// Type description
|
||||||
enum Type
|
enum Type {
|
||||||
{
|
|
||||||
World = 0x0,
|
World = 0x0,
|
||||||
Poly = 0x1,
|
Poly = 0x1,
|
||||||
Group = 0x2,
|
Group = 0x2,
|
||||||
Light = 0x4
|
Light = 0x4
|
||||||
} type;
|
} type;
|
||||||
|
@ -177,37 +152,33 @@ public:
|
||||||
float crease;
|
float crease;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details.
|
* See BaseImporter::CanRead() for details.
|
||||||
*/
|
*/
|
||||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
|
||||||
bool checkSig) const;
|
bool checkSig) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Return importer meta information.
|
/** Return importer meta information.
|
||||||
* See #BaseImporter::GetInfo for the details */
|
* See #BaseImporter::GetInfo for the details */
|
||||||
const aiImporterDesc* GetInfo () const;
|
const aiImporterDesc *GetInfo() const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
* See BaseImporter::InternReadFile() for details*/
|
* See BaseImporter::InternReadFile() for details*/
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
void InternReadFile(const std::string &pFile, aiScene *pScene,
|
||||||
IOSystem* pIOHandler);
|
IOSystem *pIOHandler);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Called prior to ReadFile().
|
/** Called prior to ReadFile().
|
||||||
* The function is a request to the importer to update its configuration
|
* The function is a request to the importer to update its configuration
|
||||||
* basing on the Importer's configuration property list.*/
|
* basing on the Importer's configuration property list.*/
|
||||||
void SetupProperties(const Importer* pImp);
|
void SetupProperties(const Importer *pImp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Get the next line from the file.
|
/** Get the next line from the file.
|
||||||
* @return false if the end of the file was reached*/
|
* @return false if the end of the file was reached*/
|
||||||
|
@ -218,7 +189,7 @@ private:
|
||||||
* load subobjects, the method returns after a 'kids 0' was
|
* load subobjects, the method returns after a 'kids 0' was
|
||||||
* encountered.
|
* encountered.
|
||||||
* @objects List of output objects*/
|
* @objects List of output objects*/
|
||||||
void LoadObjectSection(std::vector<Object>& objects);
|
void LoadObjectSection(std::vector<Object> &objects);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Convert all objects into meshes and nodes.
|
/** Convert all objects into meshes and nodes.
|
||||||
|
@ -227,24 +198,24 @@ private:
|
||||||
* @param outMaterials List of output materials
|
* @param outMaterials List of output materials
|
||||||
* @param materials Material list
|
* @param materials Material list
|
||||||
* @param Scenegraph node for the object */
|
* @param Scenegraph node for the object */
|
||||||
aiNode* ConvertObjectSection(Object& object,
|
aiNode *ConvertObjectSection(Object &object,
|
||||||
std::vector<aiMesh*>& meshes,
|
std::vector<aiMesh *> &meshes,
|
||||||
std::vector<aiMaterial*>& outMaterials,
|
std::vector<aiMaterial *> &outMaterials,
|
||||||
const std::vector<Material>& materials,
|
const std::vector<Material> &materials,
|
||||||
aiNode* parent = NULL);
|
aiNode *parent = nullptr);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Convert a material
|
/** Convert a material
|
||||||
* @param object Current object
|
* @param object Current object
|
||||||
* @param matSrc Source material description
|
* @param matSrc Source material description
|
||||||
* @param matDest Destination material to be filled */
|
* @param matDest Destination material to be filled */
|
||||||
void ConvertMaterial(const Object& object,
|
void ConvertMaterial(const Object &object,
|
||||||
const Material& matSrc,
|
const Material &matSrc,
|
||||||
aiMaterial& matDest);
|
aiMaterial &matDest);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// points to the next data line
|
// points to the next data line
|
||||||
const char* buffer;
|
const char *buffer;
|
||||||
|
|
||||||
// Configuration option: if enabled, up to two meshes
|
// Configuration option: if enabled, up to two meshes
|
||||||
// are generated per material: those faces who have
|
// are generated per material: those faces who have
|
||||||
|
@ -261,7 +232,7 @@ private:
|
||||||
unsigned int mNumMeshes;
|
unsigned int mNumMeshes;
|
||||||
|
|
||||||
// current list of light sources
|
// current list of light sources
|
||||||
std::vector<aiLight*>* mLights;
|
std::vector<aiLight *> *mLights;
|
||||||
|
|
||||||
// name counters
|
// name counters
|
||||||
unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter;
|
unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter;
|
|
@ -0,0 +1,672 @@
|
||||||
|
/*
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
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 AMFImporter.cpp
|
||||||
|
/// \brief AMF-format files importer for Assimp: main algorithm implementation.
|
||||||
|
/// \date 2016
|
||||||
|
/// \author smal.root@gmail.com
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
|
||||||
|
|
||||||
|
// Header files, Assimp.
|
||||||
|
#include "AMFImporter.hpp"
|
||||||
|
#include "AMFImporter_Macro.hpp"
|
||||||
|
|
||||||
|
#include <assimp/DefaultIOSystem.h>
|
||||||
|
#include <assimp/fast_atof.h>
|
||||||
|
|
||||||
|
// Header files, stdlib.
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
/// \var aiImporterDesc AMFImporter::Description
|
||||||
|
/// Conastant which hold importer description
|
||||||
|
const aiImporterDesc AMFImporter::Description = {
|
||||||
|
"Additive manufacturing file format(AMF) Importer",
|
||||||
|
"smalcom",
|
||||||
|
"",
|
||||||
|
"See documentation in source code. Chapter: Limitations.",
|
||||||
|
aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"amf"
|
||||||
|
};
|
||||||
|
|
||||||
|
void AMFImporter::Clear() {
|
||||||
|
mNodeElement_Cur = nullptr;
|
||||||
|
mUnit.clear();
|
||||||
|
mMaterial_Converted.clear();
|
||||||
|
mTexture_Converted.clear();
|
||||||
|
// Delete all elements
|
||||||
|
if (!mNodeElement_List.empty()) {
|
||||||
|
for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
|
||||||
|
delete ne;
|
||||||
|
}
|
||||||
|
|
||||||
|
mNodeElement_List.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMFImporter::~AMFImporter() {
|
||||||
|
if (mReader != nullptr) delete mReader;
|
||||||
|
// Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
/************************************************************ Functions: find set ************************************************************/
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
|
||||||
|
bool AMFImporter::Find_NodeElement(const std::string &pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement **pNodeElement) const {
|
||||||
|
for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
|
||||||
|
if ((ne->ID == pID) && (ne->Type == pType)) {
|
||||||
|
if (pNodeElement != nullptr) *pNodeElement = ne;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // for(CAMFImporter_NodeElement* ne: mNodeElement_List)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AMFImporter::Find_ConvertedNode(const std::string &pID, std::list<aiNode *> &pNodeList, aiNode **pNode) const {
|
||||||
|
aiString node_name(pID.c_str());
|
||||||
|
|
||||||
|
for (aiNode *node : pNodeList) {
|
||||||
|
if (node->mName == node_name) {
|
||||||
|
if (pNode != nullptr) *pNode = node;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // for(aiNode* node: pNodeList)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const {
|
||||||
|
for (const SPP_Material &mat : mMaterial_Converted) {
|
||||||
|
if (mat.ID == pID) {
|
||||||
|
if (pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // for(const SPP_Material& mat: mMaterial_Converted)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
/************************************************************ Functions: throw set ***********************************************************/
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
|
||||||
|
void AMFImporter::Throw_CloseNotFound(const std::string &pNode) {
|
||||||
|
throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Throw_IncorrectAttr(const std::string &pAttrName) {
|
||||||
|
throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Throw_IncorrectAttrValue(const std::string &pAttrName) {
|
||||||
|
throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Throw_MoreThanOnceDefined(const std::string &pNodeType, const std::string &pDescription) {
|
||||||
|
throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
|
||||||
|
throw DeadlyImportError("Not found node with name \"" + pID + "\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
/************************************************************* Functions: XML set ************************************************************/
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
|
||||||
|
void AMFImporter::XML_CheckNode_MustHaveChildren() {
|
||||||
|
if (mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string &pParentNodeName) {
|
||||||
|
static const size_t Uns_Skip_Len = 3;
|
||||||
|
const char *Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
|
||||||
|
|
||||||
|
static bool skipped_before[Uns_Skip_Len] = { false, false, false };
|
||||||
|
|
||||||
|
std::string nn(mReader->getNodeName());
|
||||||
|
bool found = false;
|
||||||
|
bool close_found = false;
|
||||||
|
size_t sk_idx;
|
||||||
|
|
||||||
|
for (sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++) {
|
||||||
|
if (nn != Uns_Skip[sk_idx]) continue;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
if (mReader->isEmptyElement()) {
|
||||||
|
close_found = true;
|
||||||
|
|
||||||
|
goto casu_cres;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mReader->read()) {
|
||||||
|
if ((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName())) {
|
||||||
|
close_found = true;
|
||||||
|
|
||||||
|
goto casu_cres;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
|
||||||
|
|
||||||
|
casu_cres:
|
||||||
|
|
||||||
|
if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
|
||||||
|
if (!close_found) Throw_CloseNotFound(nn);
|
||||||
|
|
||||||
|
if (!skipped_before[sk_idx]) {
|
||||||
|
skipped_before[sk_idx] = true;
|
||||||
|
ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AMFImporter::XML_SearchNode(const std::string &pNodeName) {
|
||||||
|
while (mReader->read()) {
|
||||||
|
if ((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx) {
|
||||||
|
std::string val(mReader->getAttributeValue(pAttrIdx));
|
||||||
|
|
||||||
|
if ((val == "false") || (val == "0"))
|
||||||
|
return false;
|
||||||
|
else if ((val == "true") || (val == "1"))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx) {
|
||||||
|
std::string val;
|
||||||
|
float tvalf;
|
||||||
|
|
||||||
|
ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
|
||||||
|
fast_atoreal_move(val.c_str(), tvalf, false);
|
||||||
|
|
||||||
|
return tvalf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx) {
|
||||||
|
return strtoul10(mReader->getAttributeValue(pAttrIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
float AMFImporter::XML_ReadNode_GetVal_AsFloat() {
|
||||||
|
std::string val;
|
||||||
|
float tvalf;
|
||||||
|
|
||||||
|
if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
|
||||||
|
if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
|
||||||
|
|
||||||
|
ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
|
||||||
|
fast_atoreal_move(val.c_str(), tvalf, false);
|
||||||
|
|
||||||
|
return tvalf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32() {
|
||||||
|
if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
|
||||||
|
if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
|
||||||
|
|
||||||
|
return strtoul10(mReader->getNodeData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::XML_ReadNode_GetVal_AsString(std::string &pValue) {
|
||||||
|
if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
|
||||||
|
if (mReader->getNodeType() != irr::io::EXN_TEXT)
|
||||||
|
throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
|
||||||
|
|
||||||
|
pValue = mReader->getNodeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
/************************************************************ Functions: parse set ***********************************************************/
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
|
||||||
|
void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement *pNode) {
|
||||||
|
mNodeElement_Cur->Child.push_back(pNode); // add new element to current element child list.
|
||||||
|
mNodeElement_Cur = pNode; // switch current element to new one.
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::ParseHelper_Node_Exit() {
|
||||||
|
// check if we can walk up.
|
||||||
|
if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) {
|
||||||
|
size_t instr_len;
|
||||||
|
|
||||||
|
pOutString.clear();
|
||||||
|
instr_len = strlen(pInStr);
|
||||||
|
if (!instr_len) return;
|
||||||
|
|
||||||
|
pOutString.reserve(instr_len * 3 / 2);
|
||||||
|
// check and correct floats in format ".x". Must be "x.y".
|
||||||
|
if (pInStr[0] == '.') pOutString.push_back('0');
|
||||||
|
|
||||||
|
pOutString.push_back(pInStr[0]);
|
||||||
|
for (size_t ci = 1; ci < instr_len; ci++) {
|
||||||
|
if ((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t'))) {
|
||||||
|
pOutString.push_back('0');
|
||||||
|
pOutString.push_back('.');
|
||||||
|
} else {
|
||||||
|
pOutString.push_back(pInStr[ci]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) {
|
||||||
|
return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const {
|
||||||
|
// With help from
|
||||||
|
// René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||||
|
const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
uint8_t tidx = 0;
|
||||||
|
uint8_t arr4[4], arr3[3];
|
||||||
|
|
||||||
|
// check input data
|
||||||
|
if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
|
||||||
|
// prepare output place
|
||||||
|
pOutputData.clear();
|
||||||
|
pOutputData.reserve(pInputBase64.size() / 4 * 3);
|
||||||
|
|
||||||
|
for (size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--) {
|
||||||
|
if (ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) {
|
||||||
|
arr4[tidx++] = pInputBase64[in_idx++];
|
||||||
|
if (tidx == 4) {
|
||||||
|
for (tidx = 0; tidx < 4; tidx++)
|
||||||
|
arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
|
||||||
|
|
||||||
|
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
|
||||||
|
arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
|
||||||
|
arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
|
||||||
|
for (tidx = 0; tidx < 3; tidx++)
|
||||||
|
pOutputData.push_back(arr3[tidx]);
|
||||||
|
|
||||||
|
tidx = 0;
|
||||||
|
} // if(tidx == 4)
|
||||||
|
} // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
|
||||||
|
else {
|
||||||
|
in_idx++;
|
||||||
|
} // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tidx) {
|
||||||
|
for (uint8_t i = tidx; i < 4; i++)
|
||||||
|
arr4[i] = 0;
|
||||||
|
for (uint8_t i = 0; i < 4; i++)
|
||||||
|
arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
|
||||||
|
|
||||||
|
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
|
||||||
|
arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
|
||||||
|
arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
|
||||||
|
for (uint8_t i = 0; i < (tidx - 1); i++)
|
||||||
|
pOutputData.push_back(arr3[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
|
||||||
|
irr::io::IrrXMLReader *OldReader = mReader; // store current XMLreader.
|
||||||
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
||||||
|
|
||||||
|
// Check whether we can read from the file
|
||||||
|
if (file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
|
||||||
|
|
||||||
|
// generate a XML reader for it
|
||||||
|
std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
|
||||||
|
mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
|
||||||
|
if (!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
|
||||||
|
//
|
||||||
|
// start reading
|
||||||
|
// search for root tag <amf>
|
||||||
|
if (XML_SearchNode("amf"))
|
||||||
|
ParseNode_Root();
|
||||||
|
else
|
||||||
|
throw DeadlyImportError("Root node \"amf\" not found.");
|
||||||
|
|
||||||
|
delete mReader;
|
||||||
|
// restore old XMLreader
|
||||||
|
mReader = OldReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <amf
|
||||||
|
// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
|
||||||
|
// version="" - Version of file format.
|
||||||
|
// >
|
||||||
|
// </amf>
|
||||||
|
// Root XML element.
|
||||||
|
// Multi elements - No.
|
||||||
|
void AMFImporter::ParseNode_Root() {
|
||||||
|
std::string unit, version;
|
||||||
|
CAMFImporter_NodeElement *ne(nullptr);
|
||||||
|
|
||||||
|
// Read attributes for node <amf>.
|
||||||
|
MACRO_ATTRREAD_LOOPBEG;
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_LOOPEND_WSKIP;
|
||||||
|
|
||||||
|
// Check attributes
|
||||||
|
if (!mUnit.empty()) {
|
||||||
|
if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create root node element.
|
||||||
|
ne = new CAMFImporter_NodeElement_Root(nullptr);
|
||||||
|
mNodeElement_Cur = ne; // set first "current" element
|
||||||
|
// and assign attribute's values
|
||||||
|
((CAMFImporter_NodeElement_Root *)ne)->Unit = unit;
|
||||||
|
((CAMFImporter_NodeElement_Root *)ne)->Version = version;
|
||||||
|
|
||||||
|
// Check for child nodes
|
||||||
|
if (!mReader->isEmptyElement()) {
|
||||||
|
MACRO_NODECHECK_LOOPBEGIN("amf");
|
||||||
|
if (XML_CheckNode_NameEqual("object")) {
|
||||||
|
ParseNode_Object();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("material")) {
|
||||||
|
ParseNode_Material();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("texture")) {
|
||||||
|
ParseNode_Texture();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("constellation")) {
|
||||||
|
ParseNode_Constellation();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("metadata")) {
|
||||||
|
ParseNode_Metadata();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MACRO_NODECHECK_LOOPEND("amf");
|
||||||
|
mNodeElement_Cur = ne; // force restore "current" element
|
||||||
|
} // if(!mReader->isEmptyElement())
|
||||||
|
|
||||||
|
mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph.
|
||||||
|
}
|
||||||
|
|
||||||
|
// <constellation
|
||||||
|
// id="" - The Object ID of the new constellation being defined.
|
||||||
|
// >
|
||||||
|
// </constellation>
|
||||||
|
// A collection of objects or constellations with specific relative locations.
|
||||||
|
// Multi elements - Yes.
|
||||||
|
// Parent element - <amf>.
|
||||||
|
void AMFImporter::ParseNode_Constellation() {
|
||||||
|
std::string id;
|
||||||
|
CAMFImporter_NodeElement *ne(nullptr);
|
||||||
|
|
||||||
|
// Read attributes for node <constellation>.
|
||||||
|
MACRO_ATTRREAD_LOOPBEG;
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_LOOPEND;
|
||||||
|
|
||||||
|
// create and if needed - define new grouping object.
|
||||||
|
ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
|
||||||
|
|
||||||
|
CAMFImporter_NodeElement_Constellation &als = *((CAMFImporter_NodeElement_Constellation *)ne); // alias for convenience
|
||||||
|
|
||||||
|
if (!id.empty()) als.ID = id;
|
||||||
|
// Check for child nodes
|
||||||
|
if (!mReader->isEmptyElement()) {
|
||||||
|
ParseHelper_Node_Enter(ne);
|
||||||
|
MACRO_NODECHECK_LOOPBEGIN("constellation");
|
||||||
|
if (XML_CheckNode_NameEqual("instance")) {
|
||||||
|
ParseNode_Instance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("metadata")) {
|
||||||
|
ParseNode_Metadata();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MACRO_NODECHECK_LOOPEND("constellation");
|
||||||
|
ParseHelper_Node_Exit();
|
||||||
|
} // if(!mReader->isEmptyElement())
|
||||||
|
else {
|
||||||
|
mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
|
||||||
|
} // if(!mReader->isEmptyElement()) else
|
||||||
|
|
||||||
|
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
|
||||||
|
}
|
||||||
|
|
||||||
|
// <instance
|
||||||
|
// objectid="" - The Object ID of the new constellation being defined.
|
||||||
|
// >
|
||||||
|
// </instance>
|
||||||
|
// A collection of objects or constellations with specific relative locations.
|
||||||
|
// Multi elements - Yes.
|
||||||
|
// Parent element - <amf>.
|
||||||
|
void AMFImporter::ParseNode_Instance() {
|
||||||
|
std::string objectid;
|
||||||
|
CAMFImporter_NodeElement *ne(nullptr);
|
||||||
|
|
||||||
|
// Read attributes for node <constellation>.
|
||||||
|
MACRO_ATTRREAD_LOOPBEG;
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_LOOPEND;
|
||||||
|
|
||||||
|
// used object id must be defined, check that.
|
||||||
|
if (objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
|
||||||
|
// create and define new grouping object.
|
||||||
|
ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
|
||||||
|
|
||||||
|
CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne); // alias for convenience
|
||||||
|
|
||||||
|
als.ObjectID = objectid;
|
||||||
|
// Check for child nodes
|
||||||
|
if (!mReader->isEmptyElement()) {
|
||||||
|
bool read_flag[6] = { false, false, false, false, false, false };
|
||||||
|
|
||||||
|
als.Delta.Set(0, 0, 0);
|
||||||
|
als.Rotation.Set(0, 0, 0);
|
||||||
|
ParseHelper_Node_Enter(ne);
|
||||||
|
MACRO_NODECHECK_LOOPBEGIN("instance");
|
||||||
|
MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
|
||||||
|
MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
|
||||||
|
MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
|
||||||
|
MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
|
||||||
|
MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
|
||||||
|
MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
|
||||||
|
MACRO_NODECHECK_LOOPEND("instance");
|
||||||
|
ParseHelper_Node_Exit();
|
||||||
|
// also convert degrees to radians.
|
||||||
|
als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
|
||||||
|
als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
|
||||||
|
als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
|
||||||
|
} // if(!mReader->isEmptyElement())
|
||||||
|
else {
|
||||||
|
mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
|
||||||
|
} // if(!mReader->isEmptyElement()) else
|
||||||
|
|
||||||
|
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
|
||||||
|
}
|
||||||
|
|
||||||
|
// <object
|
||||||
|
// id="" - A unique ObjectID for the new object being defined.
|
||||||
|
// >
|
||||||
|
// </object>
|
||||||
|
// An object definition.
|
||||||
|
// Multi elements - Yes.
|
||||||
|
// Parent element - <amf>.
|
||||||
|
void AMFImporter::ParseNode_Object() {
|
||||||
|
std::string id;
|
||||||
|
CAMFImporter_NodeElement *ne(nullptr);
|
||||||
|
|
||||||
|
// Read attributes for node <object>.
|
||||||
|
MACRO_ATTRREAD_LOOPBEG;
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_LOOPEND;
|
||||||
|
|
||||||
|
// create and if needed - define new geometry object.
|
||||||
|
ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
|
||||||
|
|
||||||
|
CAMFImporter_NodeElement_Object &als = *((CAMFImporter_NodeElement_Object *)ne); // alias for convenience
|
||||||
|
|
||||||
|
if (!id.empty()) als.ID = id;
|
||||||
|
// Check for child nodes
|
||||||
|
if (!mReader->isEmptyElement()) {
|
||||||
|
bool col_read = false;
|
||||||
|
|
||||||
|
ParseHelper_Node_Enter(ne);
|
||||||
|
MACRO_NODECHECK_LOOPBEGIN("object");
|
||||||
|
if (XML_CheckNode_NameEqual("color")) {
|
||||||
|
// Check if color already defined for object.
|
||||||
|
if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
|
||||||
|
// read data and set flag about it
|
||||||
|
ParseNode_Color();
|
||||||
|
col_read = true;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XML_CheckNode_NameEqual("mesh")) {
|
||||||
|
ParseNode_Mesh();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (XML_CheckNode_NameEqual("metadata")) {
|
||||||
|
ParseNode_Metadata();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MACRO_NODECHECK_LOOPEND("object");
|
||||||
|
ParseHelper_Node_Exit();
|
||||||
|
} // if(!mReader->isEmptyElement())
|
||||||
|
else {
|
||||||
|
mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
|
||||||
|
} // if(!mReader->isEmptyElement()) else
|
||||||
|
|
||||||
|
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
|
||||||
|
}
|
||||||
|
|
||||||
|
// <metadata
|
||||||
|
// type="" - The type of the attribute.
|
||||||
|
// >
|
||||||
|
// </metadata>
|
||||||
|
// Specify additional information about an entity.
|
||||||
|
// Multi elements - Yes.
|
||||||
|
// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
|
||||||
|
//
|
||||||
|
// Reserved types are:
|
||||||
|
// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
|
||||||
|
// "Description" - A description of the content of the entity
|
||||||
|
// "URL" - A link to an external resource relating to the entity
|
||||||
|
// "Author" - Specifies the name(s) of the author(s) of the entity
|
||||||
|
// "Company" - Specifying the company generating the entity
|
||||||
|
// "CAD" - specifies the name of the originating CAD software and version
|
||||||
|
// "Revision" - specifies the revision of the entity
|
||||||
|
// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
|
||||||
|
// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
|
||||||
|
void AMFImporter::ParseNode_Metadata() {
|
||||||
|
std::string type, value;
|
||||||
|
CAMFImporter_NodeElement *ne(nullptr);
|
||||||
|
|
||||||
|
// read attribute
|
||||||
|
MACRO_ATTRREAD_LOOPBEG;
|
||||||
|
MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
|
||||||
|
MACRO_ATTRREAD_LOOPEND;
|
||||||
|
// and value of node.
|
||||||
|
value = mReader->getNodeData();
|
||||||
|
// Create node element and assign read data.
|
||||||
|
ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
|
||||||
|
((CAMFImporter_NodeElement_Metadata *)ne)->Type = type;
|
||||||
|
((CAMFImporter_NodeElement_Metadata *)ne)->Value = value;
|
||||||
|
mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
|
||||||
|
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
/******************************************************** Functions: BaseImporter set ********************************************************/
|
||||||
|
/*********************************************************************************************************************************************/
|
||||||
|
|
||||||
|
bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const {
|
||||||
|
const std::string extension = GetExtension(pFile);
|
||||||
|
|
||||||
|
if (extension == "amf") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extension.length() || pCheckSig) {
|
||||||
|
const char *tokens[] = { "<amf" };
|
||||||
|
|
||||||
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::GetExtensionList(std::set<std::string> &pExtensionList) {
|
||||||
|
pExtensionList.insert("amf");
|
||||||
|
}
|
||||||
|
|
||||||
|
const aiImporterDesc *AMFImporter::GetInfo() const {
|
||||||
|
return &Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
|
Clear(); // delete old graph.
|
||||||
|
ParseFile(pFile, pIOHandler);
|
||||||
|
Postprocess_BuildScene(pScene);
|
||||||
|
// scene graph is ready, exit.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Assimp
|
||||||
|
|
||||||
|
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER
|
|
@ -0,0 +1,872 @@
|
||||||
|
/*
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
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 AMFImporter_Postprocess.cpp
|
||||||
|
/// \brief Convert built scenegraph and objects to Assimp scenegraph.
|
||||||
|
/// \date 2016
|
||||||
|
/// \author smal.root@gmail.com
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
|
||||||
|
|
||||||
|
#include "AMFImporter.hpp"
|
||||||
|
|
||||||
|
// Header files, Assimp.
|
||||||
|
#include <assimp/SceneCombiner.h>
|
||||||
|
#include <assimp/StandardShapes.h>
|
||||||
|
#include <assimp/StringUtils.h>
|
||||||
|
|
||||||
|
// Header files, stdlib.
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const {
|
||||||
|
aiColor4D tcol;
|
||||||
|
|
||||||
|
// Check if stored data are supported.
|
||||||
|
if (!Composition.empty()) {
|
||||||
|
throw DeadlyImportError("IME. GetColor for composition");
|
||||||
|
} else if (Color->Composed) {
|
||||||
|
throw DeadlyImportError("IME. GetColor, composed color");
|
||||||
|
} else {
|
||||||
|
tcol = Color->Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if default color must be used
|
||||||
|
if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) {
|
||||||
|
tcol.r = 0.5f;
|
||||||
|
tcol.g = 0.5f;
|
||||||
|
tcol.b = 0.5f;
|
||||||
|
tcol.a = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcol;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
|
||||||
|
std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray) const {
|
||||||
|
CAMFImporter_NodeElement_Vertices *vn = nullptr;
|
||||||
|
size_t col_idx;
|
||||||
|
|
||||||
|
// All data stored in "vertices", search for it.
|
||||||
|
for (CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
|
||||||
|
if (ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices *)ne_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If "vertices" not found then no work for us.
|
||||||
|
if (vn == nullptr) return;
|
||||||
|
|
||||||
|
pVertexCoordinateArray.reserve(vn->Child.size()); // all coordinates stored as child and we need to reserve space for future push_back's.
|
||||||
|
pVertexColorArray.resize(vn->Child.size()); // colors count equal vertices count.
|
||||||
|
col_idx = 0;
|
||||||
|
// Inside vertices collect all data and place to arrays
|
||||||
|
for (CAMFImporter_NodeElement *vn_child : vn->Child) {
|
||||||
|
// vertices, colors
|
||||||
|
if (vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex) {
|
||||||
|
// by default clear color for current vertex
|
||||||
|
pVertexColorArray[col_idx] = nullptr;
|
||||||
|
|
||||||
|
for (CAMFImporter_NodeElement *vtx : vn_child->Child) {
|
||||||
|
if (vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates) {
|
||||||
|
pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates *)vtx)->Coordinate);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtx->Type == CAMFImporter_NodeElement::ENET_Color) {
|
||||||
|
pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color *)vtx;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} // for(CAMFImporter_NodeElement* vtx: vn_child->Child)
|
||||||
|
|
||||||
|
col_idx++;
|
||||||
|
} // if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
|
||||||
|
} // for(CAMFImporter_NodeElement* vn_child: vn->Child)
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B,
|
||||||
|
const std::string &pID_A) {
|
||||||
|
size_t TextureConverted_Index;
|
||||||
|
std::string TextureConverted_ID;
|
||||||
|
|
||||||
|
// check input data
|
||||||
|
if (pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
|
||||||
|
throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
|
||||||
|
|
||||||
|
// Create ID
|
||||||
|
TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
|
||||||
|
// Check if texture specified by set of IDs is converted already.
|
||||||
|
TextureConverted_Index = 0;
|
||||||
|
for (const SPP_Texture &tex_convd : mTexture_Converted) {
|
||||||
|
if (tex_convd.ID == TextureConverted_ID) {
|
||||||
|
return TextureConverted_Index;
|
||||||
|
} else {
|
||||||
|
++TextureConverted_Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Converted texture not found, create it.
|
||||||
|
//
|
||||||
|
CAMFImporter_NodeElement_Texture *src_texture[4]{ nullptr };
|
||||||
|
std::vector<CAMFImporter_NodeElement_Texture *> src_texture_4check;
|
||||||
|
SPP_Texture converted_texture;
|
||||||
|
|
||||||
|
{ // find all specified source textures
|
||||||
|
CAMFImporter_NodeElement *t_tex;
|
||||||
|
|
||||||
|
// R
|
||||||
|
if (!pID_R.empty()) {
|
||||||
|
if (!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
|
||||||
|
|
||||||
|
src_texture[0] = (CAMFImporter_NodeElement_Texture *)t_tex;
|
||||||
|
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
|
||||||
|
} else {
|
||||||
|
src_texture[0] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// G
|
||||||
|
if (!pID_G.empty()) {
|
||||||
|
if (!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
|
||||||
|
|
||||||
|
src_texture[1] = (CAMFImporter_NodeElement_Texture *)t_tex;
|
||||||
|
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
|
||||||
|
} else {
|
||||||
|
src_texture[1] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// B
|
||||||
|
if (!pID_B.empty()) {
|
||||||
|
if (!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
|
||||||
|
|
||||||
|
src_texture[2] = (CAMFImporter_NodeElement_Texture *)t_tex;
|
||||||
|
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
|
||||||
|
} else {
|
||||||
|
src_texture[2] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A
|
||||||
|
if (!pID_A.empty()) {
|
||||||
|
if (!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
|
||||||
|
|
||||||
|
src_texture[3] = (CAMFImporter_NodeElement_Texture *)t_tex;
|
||||||
|
src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
|
||||||
|
} else {
|
||||||
|
src_texture[3] = nullptr;
|
||||||
|
}
|
||||||
|
} // END: find all specified source textures
|
||||||
|
|
||||||
|
// check that all textures has same size
|
||||||
|
if (src_texture_4check.size() > 1) {
|
||||||
|
for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++) {
|
||||||
|
if ((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
|
||||||
|
(src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth)) {
|
||||||
|
throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // if(src_texture_4check.size() > 1)
|
||||||
|
|
||||||
|
// set texture attributes
|
||||||
|
converted_texture.Width = src_texture_4check[0]->Width;
|
||||||
|
converted_texture.Height = src_texture_4check[0]->Height;
|
||||||
|
converted_texture.Depth = src_texture_4check[0]->Depth;
|
||||||
|
// if one of source texture is tiled then converted texture is tiled too.
|
||||||
|
converted_texture.Tiled = false;
|
||||||
|
for (uint8_t i = 0; i < src_texture_4check.size(); i++)
|
||||||
|
converted_texture.Tiled |= src_texture_4check[i]->Tiled;
|
||||||
|
|
||||||
|
// Create format hint.
|
||||||
|
strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string.
|
||||||
|
if (!pID_R.empty()) converted_texture.FormatHint[4] = '8';
|
||||||
|
if (!pID_G.empty()) converted_texture.FormatHint[5] = '8';
|
||||||
|
if (!pID_B.empty()) converted_texture.FormatHint[6] = '8';
|
||||||
|
if (!pID_A.empty()) converted_texture.FormatHint[7] = '8';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Сopy data of textures.
|
||||||
|
//
|
||||||
|
size_t tex_size = 0;
|
||||||
|
size_t step = 0;
|
||||||
|
size_t off_g = 0;
|
||||||
|
size_t off_b = 0;
|
||||||
|
|
||||||
|
// Calculate size of the target array and rule how data will be copied.
|
||||||
|
if (!pID_R.empty() && nullptr != src_texture[0]) {
|
||||||
|
tex_size += src_texture[0]->Data.size();
|
||||||
|
step++, off_g++, off_b++;
|
||||||
|
}
|
||||||
|
if (!pID_G.empty() && nullptr != src_texture[1]) {
|
||||||
|
tex_size += src_texture[1]->Data.size();
|
||||||
|
step++, off_b++;
|
||||||
|
}
|
||||||
|
if (!pID_B.empty() && nullptr != src_texture[2]) {
|
||||||
|
tex_size += src_texture[2]->Data.size();
|
||||||
|
step++;
|
||||||
|
}
|
||||||
|
if (!pID_A.empty() && nullptr != src_texture[3]) {
|
||||||
|
tex_size += src_texture[3]->Data.size();
|
||||||
|
step++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create target array.
|
||||||
|
converted_texture.Data = new uint8_t[tex_size];
|
||||||
|
// And copy data
|
||||||
|
auto CopyTextureData = [&](const std::string &pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void {
|
||||||
|
if (!pID.empty()) {
|
||||||
|
for (size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
|
||||||
|
CAMFImporter_NodeElement_Texture *tex = src_texture[pSrcTexNum];
|
||||||
|
ai_assert(tex);
|
||||||
|
converted_texture.Data[idx_target] = tex->Data.at(idx_src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
|
||||||
|
|
||||||
|
CopyTextureData(pID_R, 0, step, 0);
|
||||||
|
CopyTextureData(pID_G, off_g, step, 1);
|
||||||
|
CopyTextureData(pID_B, off_b, step, 2);
|
||||||
|
CopyTextureData(pID_A, step - 1, step, 3);
|
||||||
|
|
||||||
|
// Store new converted texture ID
|
||||||
|
converted_texture.ID = TextureConverted_ID;
|
||||||
|
// Store new converted texture
|
||||||
|
mTexture_Converted.push_back(converted_texture);
|
||||||
|
|
||||||
|
return TextureConverted_Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated) {
|
||||||
|
auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap *pTexMap1, const CAMFImporter_NodeElement_TexMap *pTexMap2) -> bool {
|
||||||
|
if ((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
|
||||||
|
if (pTexMap1 == nullptr) return false;
|
||||||
|
if (pTexMap2 == nullptr) return false;
|
||||||
|
|
||||||
|
if (pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
|
||||||
|
if (pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
|
||||||
|
if (pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
|
||||||
|
if (pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
pOutputList_Separated.clear();
|
||||||
|
if (pInputList.empty()) return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
SComplexFace face_start = pInputList.front();
|
||||||
|
std::list<SComplexFace> face_list_cur;
|
||||||
|
|
||||||
|
for (std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;) {
|
||||||
|
if (texmap_is_equal(face_start.TexMap, it->TexMap)) {
|
||||||
|
auto it_old = it;
|
||||||
|
|
||||||
|
++it;
|
||||||
|
face_list_cur.push_back(*it_old);
|
||||||
|
pInputList.erase(it_old);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
|
||||||
|
|
||||||
|
} while (!pInputList.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata *> &metadataList, aiNode &sceneNode) const {
|
||||||
|
if (!metadataList.empty()) {
|
||||||
|
if (sceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
|
||||||
|
|
||||||
|
// copy collected metadata to output node.
|
||||||
|
sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size()));
|
||||||
|
size_t meta_idx(0);
|
||||||
|
|
||||||
|
for (const CAMFImporter_NodeElement_Metadata &metadata : metadataList) {
|
||||||
|
sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
|
||||||
|
}
|
||||||
|
} // if(!metadataList.empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object &pNodeElement, std::list<aiMesh *> &pMeshList, aiNode **pSceneNode) {
|
||||||
|
CAMFImporter_NodeElement_Color *object_color = nullptr;
|
||||||
|
|
||||||
|
// create new aiNode and set name as <object> has.
|
||||||
|
*pSceneNode = new aiNode;
|
||||||
|
(*pSceneNode)->mName = pNodeElement.ID;
|
||||||
|
// read mesh and color
|
||||||
|
for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
|
||||||
|
std::vector<aiVector3D> vertex_arr;
|
||||||
|
std::vector<CAMFImporter_NodeElement_Color *> color_arr;
|
||||||
|
|
||||||
|
// color for object
|
||||||
|
if (ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color *)ne_child;
|
||||||
|
|
||||||
|
if (ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh) {
|
||||||
|
// Create arrays from children of mesh: vertices.
|
||||||
|
PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr);
|
||||||
|
// Use this arrays as a source when creating every aiMesh
|
||||||
|
Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
|
||||||
|
}
|
||||||
|
} // for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
|
||||||
|
const std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray,
|
||||||
|
const CAMFImporter_NodeElement_Color *pObjectColor, std::list<aiMesh *> &pMeshList, aiNode &pSceneNode) {
|
||||||
|
std::list<unsigned int> mesh_idx;
|
||||||
|
|
||||||
|
// all data stored in "volume", search for it.
|
||||||
|
for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
|
||||||
|
const CAMFImporter_NodeElement_Color *ne_volume_color = nullptr;
|
||||||
|
const SPP_Material *cur_mat = nullptr;
|
||||||
|
|
||||||
|
if (ne_child->Type == CAMFImporter_NodeElement::ENET_Volume) {
|
||||||
|
/******************* Get faces *******************/
|
||||||
|
const CAMFImporter_NodeElement_Volume *ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume *>(ne_child);
|
||||||
|
|
||||||
|
std::list<SComplexFace> complex_faces_list; // List of the faces of the volume.
|
||||||
|
std::list<std::list<SComplexFace>> complex_faces_toplist; // List of the face list for every mesh.
|
||||||
|
|
||||||
|
// check if volume use material
|
||||||
|
if (!ne_volume->MaterialID.empty()) {
|
||||||
|
if (!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// inside "volume" collect all data and place to arrays or create new objects
|
||||||
|
for (const CAMFImporter_NodeElement *ne_volume_child : ne_volume->Child) {
|
||||||
|
// color for volume
|
||||||
|
if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color) {
|
||||||
|
ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_volume_child);
|
||||||
|
} else if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle) // triangles, triangles colors
|
||||||
|
{
|
||||||
|
const CAMFImporter_NodeElement_Triangle &tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle *>(ne_volume_child);
|
||||||
|
|
||||||
|
SComplexFace complex_face;
|
||||||
|
|
||||||
|
// initialize pointers
|
||||||
|
complex_face.Color = nullptr;
|
||||||
|
complex_face.TexMap = nullptr;
|
||||||
|
// get data from triangle children: color, texture coordinates.
|
||||||
|
if (tri_al.Child.size()) {
|
||||||
|
for (const CAMFImporter_NodeElement *ne_triangle_child : tri_al.Child) {
|
||||||
|
if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
|
||||||
|
complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_triangle_child);
|
||||||
|
else if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
|
||||||
|
complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap *>(ne_triangle_child);
|
||||||
|
}
|
||||||
|
} // if(tri_al.Child.size())
|
||||||
|
|
||||||
|
// create new face and store it.
|
||||||
|
complex_face.Face.mNumIndices = 3;
|
||||||
|
complex_face.Face.mIndices = new unsigned int[3];
|
||||||
|
complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]);
|
||||||
|
complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]);
|
||||||
|
complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]);
|
||||||
|
complex_faces_list.push_back(complex_face);
|
||||||
|
}
|
||||||
|
} // for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
|
||||||
|
|
||||||
|
/**** Split faces list: one list per mesh ****/
|
||||||
|
PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
|
||||||
|
|
||||||
|
/***** Create mesh for every faces list ******/
|
||||||
|
for (std::list<SComplexFace> &face_list_cur : complex_faces_toplist) {
|
||||||
|
auto VertexIndex_GetMinimal = [](const std::list<SComplexFace> &pFaceList, const size_t *pBiggerThan) -> size_t {
|
||||||
|
size_t rv = 0;
|
||||||
|
|
||||||
|
if (pBiggerThan != nullptr) {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (const SComplexFace &face : pFaceList) {
|
||||||
|
for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) {
|
||||||
|
if (face.Face.mIndices[idx_vert] > *pBiggerThan) {
|
||||||
|
rv = face.Face.mIndices[idx_vert];
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) return *pBiggerThan;
|
||||||
|
} else {
|
||||||
|
rv = pFaceList.front().Face.mIndices[0];
|
||||||
|
} // if(pBiggerThan != nullptr) else
|
||||||
|
|
||||||
|
for (const SComplexFace &face : pFaceList) {
|
||||||
|
for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
|
||||||
|
if (face.Face.mIndices[vi] < rv) {
|
||||||
|
if (pBiggerThan != nullptr) {
|
||||||
|
if (face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
|
||||||
|
} else {
|
||||||
|
rv = face.Face.mIndices[vi];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // for(const SComplexFace& face: pFaceList)
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}; // auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
|
||||||
|
|
||||||
|
auto VertexIndex_Replace = [](std::list<SComplexFace> &pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void {
|
||||||
|
for (const SComplexFace &face : pFaceList) {
|
||||||
|
for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
|
||||||
|
if (face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
|
||||||
|
|
||||||
|
auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D {
|
||||||
|
// Color priorities(In descending order):
|
||||||
|
// 1. triangle color;
|
||||||
|
// 2. vertex color;
|
||||||
|
// 3. volume color;
|
||||||
|
// 4. object color;
|
||||||
|
// 5. material;
|
||||||
|
// 6. default - invisible coat.
|
||||||
|
//
|
||||||
|
// Fill vertices colors in color priority list above that's points from 1 to 6.
|
||||||
|
if ((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr)) // check for vertex color
|
||||||
|
{
|
||||||
|
if (pVertexColorArray[pIdx]->Composed)
|
||||||
|
throw DeadlyImportError("IME: vertex color composed");
|
||||||
|
else
|
||||||
|
return pVertexColorArray[pIdx]->Color;
|
||||||
|
} else if (ne_volume_color != nullptr) // check for volume color
|
||||||
|
{
|
||||||
|
if (ne_volume_color->Composed)
|
||||||
|
throw DeadlyImportError("IME: volume color composed");
|
||||||
|
else
|
||||||
|
return ne_volume_color->Color;
|
||||||
|
} else if (pObjectColor != nullptr) // check for object color
|
||||||
|
{
|
||||||
|
if (pObjectColor->Composed)
|
||||||
|
throw DeadlyImportError("IME: object color composed");
|
||||||
|
else
|
||||||
|
return pObjectColor->Color;
|
||||||
|
} else if (cur_mat != nullptr) // check for material
|
||||||
|
{
|
||||||
|
return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
|
||||||
|
} else // set default color.
|
||||||
|
{
|
||||||
|
return { 0, 0, 0, 0 };
|
||||||
|
} // if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
|
||||||
|
}; // auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
|
||||||
|
|
||||||
|
aiMesh *tmesh = new aiMesh;
|
||||||
|
|
||||||
|
tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // Only triangles is supported by AMF.
|
||||||
|
//
|
||||||
|
// set geometry and colors (vertices)
|
||||||
|
//
|
||||||
|
// copy faces/triangles
|
||||||
|
tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size());
|
||||||
|
tmesh->mFaces = new aiFace[tmesh->mNumFaces];
|
||||||
|
|
||||||
|
// Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
|
||||||
|
// can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
|
||||||
|
// Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
|
||||||
|
size_t VertexCount_Max = tmesh->mNumFaces * 3; // 3 - triangles.
|
||||||
|
std::vector<aiVector3D> vert_arr, texcoord_arr;
|
||||||
|
std::vector<aiColor4D> col_arr;
|
||||||
|
|
||||||
|
vert_arr.reserve(VertexCount_Max * 2); // "* 2" - see below TODO.
|
||||||
|
col_arr.reserve(VertexCount_Max * 2);
|
||||||
|
|
||||||
|
{ // fill arrays
|
||||||
|
size_t vert_idx_from, vert_idx_to;
|
||||||
|
|
||||||
|
// first iteration.
|
||||||
|
vert_idx_to = 0;
|
||||||
|
vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
|
||||||
|
vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
|
||||||
|
col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
|
||||||
|
if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
|
||||||
|
|
||||||
|
// rest iterations
|
||||||
|
do {
|
||||||
|
vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
|
||||||
|
if (vert_idx_from == vert_idx_to) break; // all indices are transferred,
|
||||||
|
|
||||||
|
vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
|
||||||
|
col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
|
||||||
|
vert_idx_to++;
|
||||||
|
if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
} // fill arrays. END.
|
||||||
|
|
||||||
|
//
|
||||||
|
// check if triangle colors are used and create additional faces if needed.
|
||||||
|
//
|
||||||
|
for (const SComplexFace &face_cur : face_list_cur) {
|
||||||
|
if (face_cur.Color != nullptr) {
|
||||||
|
aiColor4D face_color;
|
||||||
|
size_t vert_idx_new = vert_arr.size();
|
||||||
|
|
||||||
|
if (face_cur.Color->Composed)
|
||||||
|
throw DeadlyImportError("IME: face color composed");
|
||||||
|
else
|
||||||
|
face_color = face_cur.Color->Color;
|
||||||
|
|
||||||
|
for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
|
||||||
|
vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
|
||||||
|
col_arr.push_back(face_color);
|
||||||
|
face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++);
|
||||||
|
}
|
||||||
|
} // if(face_cur.Color != nullptr)
|
||||||
|
} // for(const SComplexFace& face_cur: face_list_cur)
|
||||||
|
|
||||||
|
//
|
||||||
|
// if texture is used then copy texture coordinates too.
|
||||||
|
//
|
||||||
|
if (face_list_cur.front().TexMap != nullptr) {
|
||||||
|
size_t idx_vert_new = vert_arr.size();
|
||||||
|
///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
|
||||||
|
/// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
|
||||||
|
/// optimisation.
|
||||||
|
bool *idx_vert_used;
|
||||||
|
|
||||||
|
idx_vert_used = new bool[VertexCount_Max * 2];
|
||||||
|
for (size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++)
|
||||||
|
idx_vert_used[i] = false;
|
||||||
|
|
||||||
|
// This ID's will be used when set materials ID in scene.
|
||||||
|
tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
|
||||||
|
face_list_cur.front().TexMap->TextureID_G,
|
||||||
|
face_list_cur.front().TexMap->TextureID_B,
|
||||||
|
face_list_cur.front().TexMap->TextureID_A));
|
||||||
|
texcoord_arr.resize(VertexCount_Max * 2);
|
||||||
|
for (const SComplexFace &face_cur : face_list_cur) {
|
||||||
|
for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
|
||||||
|
const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
|
||||||
|
|
||||||
|
if (!idx_vert_used[idx_vert]) {
|
||||||
|
texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
|
||||||
|
idx_vert_used[idx_vert] = true;
|
||||||
|
} else if (texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind]) {
|
||||||
|
// in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
|
||||||
|
// coordinates.
|
||||||
|
vert_arr.push_back(vert_arr.at(idx_vert));
|
||||||
|
col_arr.push_back(col_arr.at(idx_vert));
|
||||||
|
texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
|
||||||
|
face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++);
|
||||||
|
}
|
||||||
|
} // for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
|
||||||
|
} // for(const SComplexFace& face_cur: face_list_cur)
|
||||||
|
|
||||||
|
delete[] idx_vert_used;
|
||||||
|
// shrink array
|
||||||
|
texcoord_arr.resize(idx_vert_new);
|
||||||
|
} // if(face_list_cur.front().TexMap != nullptr)
|
||||||
|
|
||||||
|
//
|
||||||
|
// copy collected data to mesh
|
||||||
|
//
|
||||||
|
tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
|
||||||
|
tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
|
||||||
|
tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
|
||||||
|
|
||||||
|
memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
|
||||||
|
memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
|
||||||
|
if (texcoord_arr.size() > 0) {
|
||||||
|
tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
|
||||||
|
memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
|
||||||
|
tmesh->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D.
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx_face = 0;
|
||||||
|
for (const SComplexFace &face_cur : face_list_cur)
|
||||||
|
tmesh->mFaces[idx_face++] = face_cur.Face;
|
||||||
|
|
||||||
|
// store new aiMesh
|
||||||
|
mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size()));
|
||||||
|
pMeshList.push_back(tmesh);
|
||||||
|
} // for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
|
||||||
|
} // if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
|
||||||
|
} // for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
|
||||||
|
|
||||||
|
// if meshes was created then assign new indices with current aiNode
|
||||||
|
if (!mesh_idx.empty()) {
|
||||||
|
std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
|
||||||
|
|
||||||
|
pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size());
|
||||||
|
pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
|
||||||
|
for (size_t i = 0; i < pSceneNode.mNumMeshes; i++)
|
||||||
|
pSceneNode.mMeshes[i] = *mit++;
|
||||||
|
} // if(mesh_idx.size() > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material &pMaterial) {
|
||||||
|
SPP_Material new_mat;
|
||||||
|
|
||||||
|
new_mat.ID = pMaterial.ID;
|
||||||
|
for (const CAMFImporter_NodeElement *mat_child : pMaterial.Child) {
|
||||||
|
if (mat_child->Type == CAMFImporter_NodeElement::ENET_Color) {
|
||||||
|
new_mat.Color = (CAMFImporter_NodeElement_Color *)mat_child;
|
||||||
|
} else if (mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata) {
|
||||||
|
new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata *)mat_child);
|
||||||
|
}
|
||||||
|
} // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
|
||||||
|
|
||||||
|
// place converted material to special list
|
||||||
|
mMaterial_Converted.push_back(new_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation &pConstellation, std::list<aiNode *> &pNodeList) const {
|
||||||
|
aiNode *con_node;
|
||||||
|
std::list<aiNode *> ch_node;
|
||||||
|
|
||||||
|
// We will build next hierarchy:
|
||||||
|
// aiNode as parent (<constellation>) for set of nodes as a children
|
||||||
|
// |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
|
||||||
|
// ...
|
||||||
|
// \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
|
||||||
|
con_node = new aiNode;
|
||||||
|
con_node->mName = pConstellation.ID;
|
||||||
|
// Walk through children and search for instances of another objects, constellations.
|
||||||
|
for (const CAMFImporter_NodeElement *ne : pConstellation.Child) {
|
||||||
|
aiMatrix4x4 tmat;
|
||||||
|
aiNode *t_node;
|
||||||
|
aiNode *found_node;
|
||||||
|
|
||||||
|
if (ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
|
||||||
|
if (ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
|
||||||
|
|
||||||
|
// create alias for conveniance
|
||||||
|
CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne);
|
||||||
|
// find referenced object
|
||||||
|
if (!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
|
||||||
|
|
||||||
|
// create node for applying transformation
|
||||||
|
t_node = new aiNode;
|
||||||
|
t_node->mParent = con_node;
|
||||||
|
// apply transformation
|
||||||
|
aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
|
||||||
|
aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
|
||||||
|
aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
|
||||||
|
aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
|
||||||
|
// create array for one child node
|
||||||
|
t_node->mNumChildren = 1;
|
||||||
|
t_node->mChildren = new aiNode *[t_node->mNumChildren];
|
||||||
|
SceneCombiner::Copy(&t_node->mChildren[0], found_node);
|
||||||
|
t_node->mChildren[0]->mParent = t_node;
|
||||||
|
ch_node.push_back(t_node);
|
||||||
|
} // for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
|
||||||
|
|
||||||
|
// copy found aiNode's as children
|
||||||
|
if (ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
|
||||||
|
|
||||||
|
size_t ch_idx = 0;
|
||||||
|
|
||||||
|
con_node->mNumChildren = static_cast<unsigned int>(ch_node.size());
|
||||||
|
con_node->mChildren = new aiNode *[con_node->mNumChildren];
|
||||||
|
for (aiNode *node : ch_node)
|
||||||
|
con_node->mChildren[ch_idx++] = node;
|
||||||
|
|
||||||
|
// and place "root" of <constellation> node to node list
|
||||||
|
pNodeList.push_back(con_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
|
||||||
|
std::list<aiNode *> node_list;
|
||||||
|
std::list<aiMesh *> mesh_list;
|
||||||
|
std::list<CAMFImporter_NodeElement_Metadata *> meta_list;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
|
||||||
|
// For building aiScene we are must to do few steps:
|
||||||
|
// at first creating root node for aiScene.
|
||||||
|
pScene->mRootNode = new aiNode;
|
||||||
|
pScene->mRootNode->mParent = nullptr;
|
||||||
|
pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
|
||||||
|
// search for root(<amf>) element
|
||||||
|
CAMFImporter_NodeElement *root_el = nullptr;
|
||||||
|
|
||||||
|
for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
|
||||||
|
if (ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
|
||||||
|
|
||||||
|
root_el = ne;
|
||||||
|
|
||||||
|
break;
|
||||||
|
} // for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
|
||||||
|
|
||||||
|
// Check if root element are found.
|
||||||
|
if (root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
|
||||||
|
|
||||||
|
// after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
|
||||||
|
// <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
|
||||||
|
// at any moment.
|
||||||
|
//
|
||||||
|
// 1. <material>
|
||||||
|
// 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
|
||||||
|
for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
|
||||||
|
if (root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material *)root_child));
|
||||||
|
}
|
||||||
|
|
||||||
|
// After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
|
||||||
|
//
|
||||||
|
// 3. <object>
|
||||||
|
for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
|
||||||
|
if (root_child->Type == CAMFImporter_NodeElement::ENET_Object) {
|
||||||
|
aiNode *tnode = nullptr;
|
||||||
|
|
||||||
|
// for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
|
||||||
|
Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object *)root_child), mesh_list, &tnode);
|
||||||
|
if (tnode != nullptr) node_list.push_back(tnode);
|
||||||
|
}
|
||||||
|
} // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
||||||
|
|
||||||
|
// And finally read rest of nodes.
|
||||||
|
//
|
||||||
|
for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
|
||||||
|
// 4. <constellation>
|
||||||
|
if (root_child->Type == CAMFImporter_NodeElement::ENET_Constellation) {
|
||||||
|
// <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
|
||||||
|
Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation *)root_child), node_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5, <metadata>
|
||||||
|
if (root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata *)root_child);
|
||||||
|
} // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
|
||||||
|
|
||||||
|
// at now we can add collected metadata to root node
|
||||||
|
Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
|
||||||
|
//
|
||||||
|
// Check constellation children
|
||||||
|
//
|
||||||
|
// As said in specification:
|
||||||
|
// "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
|
||||||
|
// What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
|
||||||
|
// And at this step we are checking that relations.
|
||||||
|
nl_clean_loop:
|
||||||
|
|
||||||
|
if (node_list.size() > 1) {
|
||||||
|
// walk through all nodes
|
||||||
|
for (std::list<aiNode *>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it) {
|
||||||
|
// and try to find them in another top nodes.
|
||||||
|
std::list<aiNode *>::const_iterator next_it = nl_it;
|
||||||
|
|
||||||
|
++next_it;
|
||||||
|
for (; next_it != node_list.end(); ++next_it) {
|
||||||
|
if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) {
|
||||||
|
// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
|
||||||
|
node_list.erase(nl_it);
|
||||||
|
|
||||||
|
goto nl_clean_loop;
|
||||||
|
}
|
||||||
|
} // for(; next_it != node_list.end(); next_it++)
|
||||||
|
} // for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// move created objects to aiScene
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Nodes
|
||||||
|
if (!node_list.empty()) {
|
||||||
|
std::list<aiNode *>::const_iterator nl_it = node_list.begin();
|
||||||
|
|
||||||
|
pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
|
||||||
|
pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
|
||||||
|
for (size_t i = 0; i < pScene->mRootNode->mNumChildren; i++) {
|
||||||
|
// Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
|
||||||
|
// mRootNode only as parent.
|
||||||
|
(*nl_it)->mParent = pScene->mRootNode;
|
||||||
|
pScene->mRootNode->mChildren[i] = *nl_it++;
|
||||||
|
}
|
||||||
|
} // if(node_list.size() > 0)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Meshes
|
||||||
|
if (!mesh_list.empty()) {
|
||||||
|
std::list<aiMesh *>::const_iterator ml_it = mesh_list.begin();
|
||||||
|
|
||||||
|
pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
|
||||||
|
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
|
||||||
|
for (size_t i = 0; i < pScene->mNumMeshes; i++)
|
||||||
|
pScene->mMeshes[i] = *ml_it++;
|
||||||
|
} // if(mesh_list.size() > 0)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Textures
|
||||||
|
pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size());
|
||||||
|
if (pScene->mNumTextures > 0) {
|
||||||
|
size_t idx;
|
||||||
|
|
||||||
|
idx = 0;
|
||||||
|
pScene->mTextures = new aiTexture *[pScene->mNumTextures];
|
||||||
|
for (const SPP_Texture &tex_convd : mTexture_Converted) {
|
||||||
|
pScene->mTextures[idx] = new aiTexture;
|
||||||
|
pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width);
|
||||||
|
pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
|
||||||
|
pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data;
|
||||||
|
// texture format description.
|
||||||
|
strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
|
||||||
|
idx++;
|
||||||
|
} // for(const SPP_Texture& tex_convd: mTexture_Converted)
|
||||||
|
|
||||||
|
// Create materials for embedded textures.
|
||||||
|
idx = 0;
|
||||||
|
pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size());
|
||||||
|
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
|
||||||
|
for (const SPP_Texture &tex_convd : mTexture_Converted) {
|
||||||
|
const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + to_string(idx));
|
||||||
|
const int mode = aiTextureOp_Multiply;
|
||||||
|
const int repeat = tex_convd.Tiled ? 1 : 0;
|
||||||
|
|
||||||
|
pScene->mMaterials[idx] = new aiMaterial;
|
||||||
|
pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
|
||||||
|
pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
|
||||||
|
pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
} // if(pScene->mNumTextures > 0)
|
||||||
|
} // END: after that walk through children of root and collect data
|
||||||
|
|
||||||
|
} // namespace Assimp
|
||||||
|
|
||||||
|
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/qnan.h>
|
#include <assimp/qnan.h>
|
||||||
|
|
||||||
// ASE is quite similar to 3ds. We can reuse some structures
|
// ASE is quite similar to 3ds. We can reuse some structures
|
||||||
#include "3DS/3DSLoader.h"
|
#include "AssetLib/3DS/3DSLoader.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace ASE {
|
namespace ASE {
|
|
@ -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,
|
|
@ -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,
|
||||||
|
@ -54,16 +53,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/IOStream.hpp>
|
#include <assimp/IOStream.hpp>
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
||||||
# include <zlib.h>
|
#include <zlib.h>
|
||||||
#else
|
#else
|
||||||
# include "../contrib/zlib/zlib.h"
|
#include "../contrib/zlib/zlib.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# pragma warning(push)
|
#pragma warning(push)
|
||||||
# pragma warning(disable : 4706)
|
#pragma warning(disable : 4706)
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
@ -269,7 +268,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
|
AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
|
||||||
buffer(NULL),
|
buffer(nullptr),
|
||||||
magic(magic),
|
magic(magic),
|
||||||
container(container),
|
container(container),
|
||||||
cur_size(0),
|
cur_size(0),
|
||||||
|
@ -362,32 +361,32 @@ protected:
|
||||||
Write<uint16_t>(&chunk, (uint16_t)type);
|
Write<uint16_t>(&chunk, (uint16_t)type);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AI_BOOL:
|
case AI_BOOL:
|
||||||
Write<bool>(&chunk, *((bool *)value));
|
Write<bool>(&chunk, *((bool *)value));
|
||||||
break;
|
break;
|
||||||
case AI_INT32:
|
case AI_INT32:
|
||||||
Write<int32_t>(&chunk, *((int32_t *)value));
|
Write<int32_t>(&chunk, *((int32_t *)value));
|
||||||
break;
|
break;
|
||||||
case AI_UINT64:
|
case AI_UINT64:
|
||||||
Write<uint64_t>(&chunk, *((uint64_t *)value));
|
Write<uint64_t>(&chunk, *((uint64_t *)value));
|
||||||
break;
|
break;
|
||||||
case AI_FLOAT:
|
case AI_FLOAT:
|
||||||
Write<float>(&chunk, *((float *)value));
|
Write<float>(&chunk, *((float *)value));
|
||||||
break;
|
break;
|
||||||
case AI_DOUBLE:
|
case AI_DOUBLE:
|
||||||
Write<double>(&chunk, *((double *)value));
|
Write<double>(&chunk, *((double *)value));
|
||||||
break;
|
break;
|
||||||
case AI_AISTRING:
|
case AI_AISTRING:
|
||||||
Write<aiString>(&chunk, *((aiString *)value));
|
Write<aiString>(&chunk, *((aiString *)value));
|
||||||
break;
|
break;
|
||||||
case AI_AIVECTOR3D:
|
case AI_AIVECTOR3D:
|
||||||
Write<aiVector3D>(&chunk, *((aiVector3D *)value));
|
Write<aiVector3D>(&chunk, *((aiVector3D *)value));
|
||||||
break;
|
break;
|
||||||
#ifdef SWIG
|
#ifdef SWIG
|
||||||
case FORCE_32BIT:
|
case FORCE_32BIT:
|
||||||
#endif // SWIG
|
#endif // SWIG
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -827,7 +826,7 @@ void DumpSceneToAssbin(
|
||||||
fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
|
fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
|
@ -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,
|
||||||
|
@ -54,12 +53,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
void ASSIMP_API DumpSceneToAssbin(
|
void ASSIMP_API DumpSceneToAssbin(
|
||||||
const char* pFile,
|
const char *pFile,
|
||||||
const char* cmd,
|
const char *cmd,
|
||||||
IOSystem* pIOSystem,
|
IOSystem *pIOSystem,
|
||||||
const aiScene* pScene,
|
const aiScene *pScene,
|
||||||
bool shortened,
|
bool shortened,
|
||||||
bool compressed);
|
bool compressed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
|
||||||
|
|
||||||
// internal headers
|
// internal headers
|
||||||
#include "Assbin/AssbinLoader.h"
|
#include "AssetLib/Assbin/AssbinLoader.h"
|
||||||
#include "Common/assbin_chunks.h"
|
#include "Common/assbin_chunks.h"
|
||||||
#include <assimp/MemoryIOWrapper.h>
|
#include <assimp/MemoryIOWrapper.h>
|
||||||
#include <assimp/anim.h>
|
#include <assimp/anim.h>
|
||||||
|
@ -253,32 +253,32 @@ void AssbinImporter::ReadBinaryNode(IOStream *stream, aiNode **onode, aiNode *pa
|
||||||
void *data = nullptr;
|
void *data = nullptr;
|
||||||
|
|
||||||
switch (node->mMetaData->mValues[i].mType) {
|
switch (node->mMetaData->mValues[i].mType) {
|
||||||
case AI_BOOL:
|
case AI_BOOL:
|
||||||
data = new bool(Read<bool>(stream));
|
data = new bool(Read<bool>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_INT32:
|
case AI_INT32:
|
||||||
data = new int32_t(Read<int32_t>(stream));
|
data = new int32_t(Read<int32_t>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_UINT64:
|
case AI_UINT64:
|
||||||
data = new uint64_t(Read<uint64_t>(stream));
|
data = new uint64_t(Read<uint64_t>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_FLOAT:
|
case AI_FLOAT:
|
||||||
data = new ai_real(Read<ai_real>(stream));
|
data = new ai_real(Read<ai_real>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_DOUBLE:
|
case AI_DOUBLE:
|
||||||
data = new double(Read<double>(stream));
|
data = new double(Read<double>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_AISTRING:
|
case AI_AISTRING:
|
||||||
data = new aiString(Read<aiString>(stream));
|
data = new aiString(Read<aiString>(stream));
|
||||||
break;
|
break;
|
||||||
case AI_AIVECTOR3D:
|
case AI_AIVECTOR3D:
|
||||||
data = new aiVector3D(Read<aiVector3D>(stream));
|
data = new aiVector3D(Read<aiVector3D>(stream));
|
||||||
break;
|
break;
|
||||||
#ifndef SWIG
|
#ifndef SWIG
|
||||||
case FORCE_32BIT:
|
case FORCE_32BIT:
|
||||||
#endif // SWIG
|
#endif // SWIG
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
node->mMetaData->mValues[i].mData = data;
|
node->mMetaData->mValues[i].mData = data;
|
|
@ -9,30 +9,31 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information
|
||||||
#ifndef ASSIMP_BUILD_NO_EXPORT
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
||||||
|
|
||||||
#include <assimp/Importer.hpp>
|
#include <assimp/scene.h>
|
||||||
#include <assimp/Exporter.hpp>
|
#include <assimp/Exporter.hpp>
|
||||||
#include <assimp/IOStream.hpp>
|
#include <assimp/IOStream.hpp>
|
||||||
#include <assimp/IOSystem.hpp>
|
#include <assimp/IOSystem.hpp>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/Exceptional.h>
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <limits>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#define CURRENT_FORMAT_VERSION 100
|
#define CURRENT_FORMAT_VERSION 100
|
||||||
|
|
||||||
// grab scoped_ptr from assimp to avoid a dependency on boost.
|
// grab scoped_ptr from assimp to avoid a dependency on boost.
|
||||||
//#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
|
//#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
#include "mesh_splitter.h"
|
#include "mesh_splitter.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "cencode.h"
|
#include "cencode.h"
|
||||||
}
|
}
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
|
void ExportAssimp2Json(const char *, Assimp::IOSystem *, const aiScene *, const Assimp::ExportProperties *);
|
||||||
|
|
||||||
// small utility class to simplify serializing the aiScene to Json
|
// small utility class to simplify serializing the aiScene to Json
|
||||||
class JSONWriter {
|
class JSONWriter {
|
||||||
|
@ -42,10 +43,8 @@ public:
|
||||||
Flag_WriteSpecialFloats = 0x2,
|
Flag_WriteSpecialFloats = 0x2,
|
||||||
};
|
};
|
||||||
|
|
||||||
JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
|
JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
|
||||||
: out(out)
|
out(out), first(), flags(flags) {
|
||||||
, first()
|
|
||||||
, flags(flags) {
|
|
||||||
// make sure that all formatting happens using the standard, C locale and not the user's current locale
|
// make sure that all formatting happens using the standard, C locale and not the user's current locale
|
||||||
buff.imbue(std::locale("C"));
|
buff.imbue(std::locale("C"));
|
||||||
}
|
}
|
||||||
|
@ -68,30 +67,30 @@ public:
|
||||||
indent.erase(indent.end() - 1);
|
indent.erase(indent.end() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Key(const std::string& name) {
|
void Key(const std::string &name) {
|
||||||
AddIndentation();
|
AddIndentation();
|
||||||
Delimit();
|
Delimit();
|
||||||
buff << '\"' + name + "\": ";
|
buff << '\"' + name + "\": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Literal>
|
template <typename Literal>
|
||||||
void Element(const Literal& name) {
|
void Element(const Literal &name) {
|
||||||
AddIndentation();
|
AddIndentation();
|
||||||
Delimit();
|
Delimit();
|
||||||
|
|
||||||
LiteralToString(buff, name) << '\n';
|
LiteralToString(buff, name) << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Literal>
|
template <typename Literal>
|
||||||
void SimpleValue(const Literal& s) {
|
void SimpleValue(const Literal &s) {
|
||||||
LiteralToString(buff, s) << '\n';
|
LiteralToString(buff, s) << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleValue(const void* buffer, size_t len) {
|
void SimpleValue(const void *buffer, size_t len) {
|
||||||
base64_encodestate s;
|
base64_encodestate s;
|
||||||
base64_init_encodestate(&s);
|
base64_init_encodestate(&s);
|
||||||
|
|
||||||
char* const cur_out = new char[std::max(len * 2, static_cast<size_t>(16u))];
|
char *const cur_out = new char[std::max(len * 2, static_cast<size_t>(16u))];
|
||||||
const int n = base64_encode_block(reinterpret_cast<const char *>(buffer), static_cast<int>(len), cur_out, &s);
|
const int n = base64_encode_block(reinterpret_cast<const char *>(buffer), static_cast<int>(len), cur_out, &s);
|
||||||
cur_out[n + base64_encode_blockend(cur_out + n, &s)] = '\0';
|
cur_out[n + base64_encode_blockend(cur_out + n, &s)] = '\0';
|
||||||
|
|
||||||
|
@ -156,21 +155,20 @@ public:
|
||||||
void Delimit() {
|
void Delimit() {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
buff << ',';
|
buff << ',';
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
buff << ' ';
|
buff << ' ';
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Literal>
|
template <typename Literal>
|
||||||
std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
|
std::stringstream &LiteralToString(std::stringstream &stream, const Literal &s) {
|
||||||
stream << s;
|
stream << s;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
|
std::stringstream &LiteralToString(std::stringstream &stream, const aiString &s) {
|
||||||
std::string t;
|
std::string t;
|
||||||
|
|
||||||
// escape backslashes and single quotes, both would render the JSON invalid if left as is
|
// escape backslashes and single quotes, both would render the JSON invalid if left as is
|
||||||
|
@ -189,10 +187,10 @@ private:
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream& LiteralToString(std::stringstream& stream, float f) {
|
std::stringstream &LiteralToString(std::stringstream &stream, float f) {
|
||||||
if (!std::numeric_limits<float>::is_iec559) {
|
if (!std::numeric_limits<float>::is_iec559) {
|
||||||
// on a non IEEE-754 platform, we make no assumptions about the representation or existence
|
// on a non IEEE-754 platform, we make no assumptions about the representation or existence
|
||||||
// of special floating-point numbers.
|
// of special floating-point numbers.
|
||||||
stream << f;
|
stream << f;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -228,7 +226,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Assimp::IOStream& out;
|
Assimp::IOStream &out;
|
||||||
std::string indent, newline;
|
std::string indent, newline;
|
||||||
std::stringstream buff;
|
std::stringstream buff;
|
||||||
bool first;
|
bool first;
|
||||||
|
@ -236,7 +234,7 @@ private:
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiVector3D &ai, bool is_elem = true) {
|
||||||
out.StartArray(is_elem);
|
out.StartArray(is_elem);
|
||||||
out.Element(ai.x);
|
out.Element(ai.x);
|
||||||
out.Element(ai.y);
|
out.Element(ai.y);
|
||||||
|
@ -244,7 +242,7 @@ void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
|
||||||
out.EndArray();
|
out.EndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiQuaternion &ai, bool is_elem = true) {
|
||||||
out.StartArray(is_elem);
|
out.StartArray(is_elem);
|
||||||
out.Element(ai.w);
|
out.Element(ai.w);
|
||||||
out.Element(ai.x);
|
out.Element(ai.x);
|
||||||
|
@ -253,7 +251,7 @@ void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
|
||||||
out.EndArray();
|
out.EndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiColor3D &ai, bool is_elem = true) {
|
||||||
out.StartArray(is_elem);
|
out.StartArray(is_elem);
|
||||||
out.Element(ai.r);
|
out.Element(ai.r);
|
||||||
out.Element(ai.g);
|
out.Element(ai.g);
|
||||||
|
@ -261,7 +259,7 @@ void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
|
||||||
out.EndArray();
|
out.EndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiMatrix4x4 &ai, bool is_elem = true) {
|
||||||
out.StartArray(is_elem);
|
out.StartArray(is_elem);
|
||||||
for (unsigned int x = 0; x < 4; ++x) {
|
for (unsigned int x = 0; x < 4; ++x) {
|
||||||
for (unsigned int y = 0; y < 4; ++y) {
|
for (unsigned int y = 0; y < 4; ++y) {
|
||||||
|
@ -271,7 +269,7 @@ void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
|
||||||
out.EndArray();
|
out.EndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiBone &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -292,7 +290,7 @@ void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiFace &ai, bool is_elem = true) {
|
||||||
out.StartArray(is_elem);
|
out.StartArray(is_elem);
|
||||||
for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
|
for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
|
||||||
out.Element(ai.mIndices[i]);
|
out.Element(ai.mIndices[i]);
|
||||||
|
@ -300,7 +298,7 @@ void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
|
||||||
out.EndArray();
|
out.EndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiMesh &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -411,7 +409,7 @@ void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiNode &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -441,13 +439,13 @@ void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiMaterial &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("properties");
|
out.Key("properties");
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
|
for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
|
||||||
const aiMaterialProperty* const prop = ai.mProperties[i];
|
const aiMaterialProperty *const prop = ai.mProperties[i];
|
||||||
out.StartObj(true);
|
out.StartObj(true);
|
||||||
out.Key("key");
|
out.Key("key");
|
||||||
out.SimpleValue(prop->mKey);
|
out.SimpleValue(prop->mKey);
|
||||||
|
@ -461,46 +459,41 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
|
||||||
|
|
||||||
out.Key("value");
|
out.Key("value");
|
||||||
switch (prop->mType) {
|
switch (prop->mType) {
|
||||||
case aiPTI_Float:
|
case aiPTI_Float:
|
||||||
if (prop->mDataLength / sizeof(float) > 1) {
|
if (prop->mDataLength / sizeof(float) > 1) {
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(float); ++ii) {
|
for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(float); ++ii) {
|
||||||
out.Element(reinterpret_cast<float*>(prop->mData)[ii]);
|
out.Element(reinterpret_cast<float *>(prop->mData)[ii]);
|
||||||
}
|
|
||||||
out.EndArray();
|
|
||||||
}
|
}
|
||||||
else {
|
out.EndArray();
|
||||||
out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
|
} else {
|
||||||
}
|
out.SimpleValue(*reinterpret_cast<float *>(prop->mData));
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case aiPTI_Integer:
|
case aiPTI_Integer:
|
||||||
if (prop->mDataLength / sizeof(int) > 1) {
|
if (prop->mDataLength / sizeof(int) > 1) {
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(int); ++ii) {
|
for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(int); ++ii) {
|
||||||
out.Element(reinterpret_cast<int*>(prop->mData)[ii]);
|
out.Element(reinterpret_cast<int *>(prop->mData)[ii]);
|
||||||
}
|
|
||||||
out.EndArray();
|
|
||||||
} else {
|
|
||||||
out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
|
|
||||||
}
|
}
|
||||||
break;
|
out.EndArray();
|
||||||
|
} else {
|
||||||
|
out.SimpleValue(*reinterpret_cast<int *>(prop->mData));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case aiPTI_String:
|
case aiPTI_String: {
|
||||||
{
|
aiString s;
|
||||||
aiString s;
|
aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
|
||||||
aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
|
out.SimpleValue(s);
|
||||||
out.SimpleValue(s);
|
} break;
|
||||||
}
|
case aiPTI_Buffer: {
|
||||||
break;
|
// binary data is written as series of hex-encoded octets
|
||||||
case aiPTI_Buffer:
|
out.SimpleValue(prop->mData, prop->mDataLength);
|
||||||
{
|
} break;
|
||||||
// binary data is written as series of hex-encoded octets
|
default:
|
||||||
out.SimpleValue(prop->mData, prop->mDataLength);
|
assert(false);
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
|
@ -510,7 +503,7 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiTexture &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("width");
|
out.Key("width");
|
||||||
|
@ -525,13 +518,12 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
|
||||||
out.Key("data");
|
out.Key("data");
|
||||||
if (!ai.mHeight) {
|
if (!ai.mHeight) {
|
||||||
out.SimpleValue(ai.pcData, ai.mWidth);
|
out.SimpleValue(ai.pcData, ai.mWidth);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int y = 0; y < ai.mHeight; ++y) {
|
for (unsigned int y = 0; y < ai.mHeight; ++y) {
|
||||||
out.StartArray(true);
|
out.StartArray(true);
|
||||||
for (unsigned int x = 0; x < ai.mWidth; ++x) {
|
for (unsigned int x = 0; x < ai.mWidth; ++x) {
|
||||||
const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
|
const aiTexel &tx = ai.pcData[y * ai.mWidth + x];
|
||||||
out.StartArray(true);
|
out.StartArray(true);
|
||||||
out.Element(static_cast<unsigned int>(tx.r));
|
out.Element(static_cast<unsigned int>(tx.r));
|
||||||
out.Element(static_cast<unsigned int>(tx.g));
|
out.Element(static_cast<unsigned int>(tx.g));
|
||||||
|
@ -547,7 +539,7 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiLight &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -585,7 +577,6 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
|
||||||
if (ai.mType != aiLightSource_POINT) {
|
if (ai.mType != aiLightSource_POINT) {
|
||||||
out.Key("direction");
|
out.Key("direction");
|
||||||
Write(out, ai.mDirection, false);
|
Write(out, ai.mDirection, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ai.mType != aiLightSource_DIRECTIONAL) {
|
if (ai.mType != aiLightSource_DIRECTIONAL) {
|
||||||
|
@ -596,7 +587,7 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiNodeAnim &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -612,7 +603,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
|
||||||
out.Key("positionkeys");
|
out.Key("positionkeys");
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
|
for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
|
||||||
const aiVectorKey& pos = ai.mPositionKeys[n];
|
const aiVectorKey &pos = ai.mPositionKeys[n];
|
||||||
out.StartArray(true);
|
out.StartArray(true);
|
||||||
out.Element(pos.mTime);
|
out.Element(pos.mTime);
|
||||||
Write(out, pos.mValue);
|
Write(out, pos.mValue);
|
||||||
|
@ -625,7 +616,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
|
||||||
out.Key("rotationkeys");
|
out.Key("rotationkeys");
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
|
for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
|
||||||
const aiQuatKey& rot = ai.mRotationKeys[n];
|
const aiQuatKey &rot = ai.mRotationKeys[n];
|
||||||
out.StartArray(true);
|
out.StartArray(true);
|
||||||
out.Element(rot.mTime);
|
out.Element(rot.mTime);
|
||||||
Write(out, rot.mValue);
|
Write(out, rot.mValue);
|
||||||
|
@ -638,7 +629,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
|
||||||
out.Key("scalingkeys");
|
out.Key("scalingkeys");
|
||||||
out.StartArray();
|
out.StartArray();
|
||||||
for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
|
for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
|
||||||
const aiVectorKey& scl = ai.mScalingKeys[n];
|
const aiVectorKey &scl = ai.mScalingKeys[n];
|
||||||
out.StartArray(true);
|
out.StartArray(true);
|
||||||
out.Element(scl.mTime);
|
out.Element(scl.mTime);
|
||||||
Write(out, scl.mValue);
|
Write(out, scl.mValue);
|
||||||
|
@ -649,7 +640,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiAnimation &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -670,7 +661,7 @@ void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
|
void Write(JSONWriter &out, const aiCamera &ai, bool is_elem = true) {
|
||||||
out.StartObj(is_elem);
|
out.StartObj(is_elem);
|
||||||
|
|
||||||
out.Key("name");
|
out.Key("name");
|
||||||
|
@ -697,7 +688,7 @@ void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteFormatInfo(JSONWriter& out) {
|
void WriteFormatInfo(JSONWriter &out) {
|
||||||
out.StartObj();
|
out.StartObj();
|
||||||
out.Key("format");
|
out.Key("format");
|
||||||
out.SimpleValue("\"assimp2json\"");
|
out.SimpleValue("\"assimp2json\"");
|
||||||
|
@ -706,7 +697,7 @@ void WriteFormatInfo(JSONWriter& out) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(JSONWriter& out, const aiScene& ai) {
|
void Write(JSONWriter &out, const aiScene &ai) {
|
||||||
out.StartObj();
|
out.StartObj();
|
||||||
|
|
||||||
out.Key("__metadata__");
|
out.Key("__metadata__");
|
||||||
|
@ -774,15 +765,14 @@ void Write(JSONWriter& out, const aiScene& ai) {
|
||||||
out.EndObj();
|
out.EndObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) {
|
||||||
void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) {
|
|
||||||
std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
|
std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
|
||||||
if (!str) {
|
if (!str) {
|
||||||
//throw Assimp::DeadlyExportError("could not open output file");
|
throw DeadlyExportError("could not open output file");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a copy of the scene so we can modify it
|
// get a copy of the scene so we can modify it
|
||||||
aiScene* scenecopy_tmp;
|
aiScene *scenecopy_tmp;
|
||||||
aiCopyScene(scene, &scenecopy_tmp);
|
aiCopyScene(scene, &scenecopy_tmp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -795,15 +785,14 @@ void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* sc
|
||||||
JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
|
JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
|
||||||
Write(s, *scenecopy_tmp);
|
Write(s, *scenecopy_tmp);
|
||||||
|
|
||||||
}
|
} catch (...) {
|
||||||
catch (...) {
|
|
||||||
aiFreeScene(scenecopy_tmp);
|
aiFreeScene(scenecopy_tmp);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
aiFreeScene(scenecopy_tmp);
|
aiFreeScene(scenecopy_tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Assimp
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
|
||||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
|
@ -0,0 +1,659 @@
|
||||||
|
/*
|
||||||
|
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 AssxmlFileWriter.cpp
|
||||||
|
* @brief Implementation of Assxml file writer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AssxmlFileWriter.h"
|
||||||
|
|
||||||
|
#include "PostProcessing/ProcessHelper.h"
|
||||||
|
|
||||||
|
#include <assimp/version.h>
|
||||||
|
#include <assimp/Exporter.hpp>
|
||||||
|
#include <assimp/IOStream.hpp>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
||||||
|
#include <zlib.h>
|
||||||
|
#else
|
||||||
|
#include <contrib/zlib/zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
namespace AssxmlFileWriter {
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
static int ioprintf(IOStream *io, const char *format, ...) {
|
||||||
|
using namespace std;
|
||||||
|
if (nullptr == io) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int Size = 4096;
|
||||||
|
char sz[Size];
|
||||||
|
::memset(sz, '\0', Size);
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
const unsigned int nSize = vsnprintf(sz, Size - 1, format, va);
|
||||||
|
ai_assert(nSize < Size);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
io->Write(sz, sizeof(char), nSize);
|
||||||
|
|
||||||
|
return nSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Convert a name to standard XML format
|
||||||
|
static void ConvertName(aiString &out, const aiString &in) {
|
||||||
|
out.length = 0;
|
||||||
|
for (unsigned int i = 0; i < in.length; ++i) {
|
||||||
|
switch (in.data[i]) {
|
||||||
|
case '<':
|
||||||
|
out.Append("<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
out.Append(">");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
out.Append("&");
|
||||||
|
break;
|
||||||
|
case '\"':
|
||||||
|
out.Append(""");
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
out.Append("'");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out.data[out.length++] = in.data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.data[out.length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Write a single node as text dump
|
||||||
|
static void WriteNode(const aiNode *node, IOStream *io, unsigned int depth) {
|
||||||
|
char prefix[512];
|
||||||
|
for (unsigned int i = 0; i < depth; ++i)
|
||||||
|
prefix[i] = '\t';
|
||||||
|
prefix[depth] = '\0';
|
||||||
|
|
||||||
|
const aiMatrix4x4 &m = node->mTransformation;
|
||||||
|
|
||||||
|
aiString name;
|
||||||
|
ConvertName(name, node->mName);
|
||||||
|
ioprintf(io, "%s<Node name=\"%s\"> \n"
|
||||||
|
"%s\t<Matrix4> \n"
|
||||||
|
"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"%s\t</Matrix4> \n",
|
||||||
|
prefix, name.data, prefix,
|
||||||
|
prefix, m.a1, m.a2, m.a3, m.a4,
|
||||||
|
prefix, m.b1, m.b2, m.b3, m.b4,
|
||||||
|
prefix, m.c1, m.c2, m.c3, m.c4,
|
||||||
|
prefix, m.d1, m.d2, m.d3, m.d4, prefix);
|
||||||
|
|
||||||
|
if (node->mNumMeshes) {
|
||||||
|
ioprintf(io, "%s\t<MeshRefs num=\"%u\">\n%s\t",
|
||||||
|
prefix, node->mNumMeshes, prefix);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
|
||||||
|
ioprintf(io, "%u ", node->mMeshes[i]);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\n%s\t</MeshRefs>\n", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->mNumChildren) {
|
||||||
|
ioprintf(io, "%s\t<NodeList num=\"%u\">\n",
|
||||||
|
prefix, node->mNumChildren);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
||||||
|
WriteNode(node->mChildren[i], io, depth + 2);
|
||||||
|
}
|
||||||
|
ioprintf(io, "%s\t</NodeList>\n", prefix);
|
||||||
|
}
|
||||||
|
ioprintf(io, "%s</Node>\n", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Some chuncks of text will need to be encoded for XML
|
||||||
|
// http://stackoverflow.com/questions/5665231/most-efficient-way-to-escape-xml-html-in-c-string#5665377
|
||||||
|
static std::string encodeXML(const std::string &data) {
|
||||||
|
std::string buffer;
|
||||||
|
buffer.reserve(data.size());
|
||||||
|
for (size_t pos = 0; pos != data.size(); ++pos) {
|
||||||
|
switch (data[pos]) {
|
||||||
|
case '&': buffer.append("&"); break;
|
||||||
|
case '\"': buffer.append("""); break;
|
||||||
|
case '\'': buffer.append("'"); break;
|
||||||
|
case '<': buffer.append("<"); break;
|
||||||
|
case '>': buffer.append(">"); break;
|
||||||
|
default: buffer.append(&data[pos], 1); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Write a text model dump
|
||||||
|
static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene, IOStream *io, bool shortened) {
|
||||||
|
time_t tt = ::time(NULL);
|
||||||
|
#if _WIN32
|
||||||
|
tm *p = gmtime(&tt);
|
||||||
|
#else
|
||||||
|
struct tm now;
|
||||||
|
tm *p = gmtime_r(&tt, &now);
|
||||||
|
#endif
|
||||||
|
ai_assert(nullptr != p);
|
||||||
|
|
||||||
|
std::string c = cmd;
|
||||||
|
std::string::size_type s;
|
||||||
|
|
||||||
|
// https://sourceforge.net/tracker/?func=detail&aid=3167364&group_id=226462&atid=1067632
|
||||||
|
// -- not allowed in XML comments
|
||||||
|
while ((s = c.find("--")) != std::string::npos) {
|
||||||
|
c[s] = '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
// write header
|
||||||
|
std::string header(
|
||||||
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||||
|
"<ASSIMP format_id=\"1\">\n\n"
|
||||||
|
"<!-- XML Model dump produced by assimp dump\n"
|
||||||
|
" Library version: %u.%u.%u\n"
|
||||||
|
" Source: %s\n"
|
||||||
|
" Command line: %s\n"
|
||||||
|
" %s\n"
|
||||||
|
"-->"
|
||||||
|
" \n\n"
|
||||||
|
"<Scene flags=\"%u\" postprocessing=\"%u\">\n");
|
||||||
|
|
||||||
|
const unsigned int majorVersion(aiGetVersionMajor());
|
||||||
|
const unsigned int minorVersion(aiGetVersionMinor());
|
||||||
|
const unsigned int rev(aiGetVersionRevision());
|
||||||
|
const char *curtime(asctime(p));
|
||||||
|
ioprintf(io, header.c_str(), majorVersion, minorVersion, rev, pFile, c.c_str(), curtime, scene->mFlags, 0u);
|
||||||
|
|
||||||
|
// write the node graph
|
||||||
|
WriteNode(scene->mRootNode, io, 0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// write cameras
|
||||||
|
for (unsigned int i = 0; i < scene->mNumCameras;++i) {
|
||||||
|
aiCamera* cam = scene->mCameras[i];
|
||||||
|
ConvertName(name,cam->mName);
|
||||||
|
|
||||||
|
// camera header
|
||||||
|
ioprintf(io,"\t<Camera parent=\"%s\">\n"
|
||||||
|
"\t\t<Vector3 name=\"up\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Float name=\"fov\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"aspect\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"near_clip\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"far_clip\" > %f </Float>\n"
|
||||||
|
"\t</Camera>\n",
|
||||||
|
name.data,
|
||||||
|
cam->mUp.x,cam->mUp.y,cam->mUp.z,
|
||||||
|
cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z,
|
||||||
|
cam->mPosition.x,cam->mPosition.y,cam->mPosition.z,
|
||||||
|
cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write lights
|
||||||
|
for (unsigned int i = 0; i < scene->mNumLights;++i) {
|
||||||
|
aiLight* l = scene->mLights[i];
|
||||||
|
ConvertName(name,l->mName);
|
||||||
|
|
||||||
|
// light header
|
||||||
|
ioprintf(io,"\t<Light parent=\"%s\"> type=\"%s\"\n"
|
||||||
|
"\t\t<Vector3 name=\"diffuse\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Vector3 name=\"specular\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Vector3 name=\"ambient\" > %0 8f %0 8f %0 8f </Vector3>\n",
|
||||||
|
name.data,
|
||||||
|
(l->mType == aiLightSource_DIRECTIONAL ? "directional" :
|
||||||
|
(l->mType == aiLightSource_POINT ? "point" : "spot" )),
|
||||||
|
l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b,
|
||||||
|
l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b,
|
||||||
|
l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b);
|
||||||
|
|
||||||
|
if (l->mType != aiLightSource_DIRECTIONAL) {
|
||||||
|
ioprintf(io,
|
||||||
|
"\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n"
|
||||||
|
"\t\t<Float name=\"atten_cst\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"atten_lin\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"atten_sqr\" > %f </Float>\n",
|
||||||
|
l->mPosition.x,l->mPosition.y,l->mPosition.z,
|
||||||
|
l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l->mType != aiLightSource_POINT) {
|
||||||
|
ioprintf(io,
|
||||||
|
"\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n",
|
||||||
|
l->mDirection.x,l->mDirection.y,l->mDirection.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l->mType == aiLightSource_SPOT) {
|
||||||
|
ioprintf(io,
|
||||||
|
"\t\t<Float name=\"cone_out\" > %f </Float>\n"
|
||||||
|
"\t\t<Float name=\"cone_inn\" > %f </Float>\n",
|
||||||
|
l->mAngleOuterCone,l->mAngleInnerCone);
|
||||||
|
}
|
||||||
|
ioprintf(io,"\t</Light>\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
aiString name;
|
||||||
|
|
||||||
|
// write textures
|
||||||
|
if (scene->mNumTextures) {
|
||||||
|
ioprintf(io, "<TextureList num=\"%u\">\n", scene->mNumTextures);
|
||||||
|
for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
|
||||||
|
aiTexture *tex = scene->mTextures[i];
|
||||||
|
bool compressed = (tex->mHeight == 0);
|
||||||
|
|
||||||
|
// mesh header
|
||||||
|
ioprintf(io, "\t<Texture width=\"%u\" height=\"%u\" compressed=\"%s\"> \n",
|
||||||
|
(compressed ? -1 : tex->mWidth), (compressed ? -1 : tex->mHeight),
|
||||||
|
(compressed ? "true" : "false"));
|
||||||
|
|
||||||
|
if (compressed) {
|
||||||
|
ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth);
|
||||||
|
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < tex->mWidth; ++n) {
|
||||||
|
ioprintf(io, "\t\t\t%2x", reinterpret_cast<uint8_t *>(tex->pcData)[n]);
|
||||||
|
if (n && !(n % 50)) {
|
||||||
|
ioprintf(io, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!shortened) {
|
||||||
|
ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth * tex->mHeight * 4);
|
||||||
|
|
||||||
|
// const unsigned int width = (unsigned int)std::log10((double)std::max(tex->mHeight,tex->mWidth))+1;
|
||||||
|
for (unsigned int y = 0; y < tex->mHeight; ++y) {
|
||||||
|
for (unsigned int x = 0; x < tex->mWidth; ++x) {
|
||||||
|
aiTexel *tx = tex->pcData + y * tex->mWidth + x;
|
||||||
|
unsigned int r = tx->r, g = tx->g, b = tx->b, a = tx->a;
|
||||||
|
ioprintf(io, "\t\t\t%2x %2x %2x %2x", r, g, b, a);
|
||||||
|
|
||||||
|
// group by four for readability
|
||||||
|
if (0 == (x + y * tex->mWidth) % 4) {
|
||||||
|
ioprintf(io, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Data>\n\t</Texture>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "</TextureList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write materials
|
||||||
|
if (scene->mNumMaterials) {
|
||||||
|
ioprintf(io, "<MaterialList num=\"%u\">\n", scene->mNumMaterials);
|
||||||
|
for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
|
||||||
|
const aiMaterial *mat = scene->mMaterials[i];
|
||||||
|
|
||||||
|
ioprintf(io, "\t<Material>\n");
|
||||||
|
ioprintf(io, "\t\t<MatPropertyList num=\"%u\">\n", mat->mNumProperties);
|
||||||
|
for (unsigned int n = 0; n < mat->mNumProperties; ++n) {
|
||||||
|
|
||||||
|
const aiMaterialProperty *prop = mat->mProperties[n];
|
||||||
|
const char *sz = "";
|
||||||
|
if (prop->mType == aiPTI_Float) {
|
||||||
|
sz = "float";
|
||||||
|
} else if (prop->mType == aiPTI_Integer) {
|
||||||
|
sz = "integer";
|
||||||
|
} else if (prop->mType == aiPTI_String) {
|
||||||
|
sz = "string";
|
||||||
|
} else if (prop->mType == aiPTI_Buffer) {
|
||||||
|
sz = "binary_buffer";
|
||||||
|
}
|
||||||
|
|
||||||
|
ioprintf(io, "\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%u\"",
|
||||||
|
prop->mKey.data, sz,
|
||||||
|
::TextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
|
||||||
|
|
||||||
|
if (prop->mType == aiPTI_Float) {
|
||||||
|
ioprintf(io, " size=\"%i\">\n\t\t\t\t",
|
||||||
|
static_cast<int>(prop->mDataLength / sizeof(float)));
|
||||||
|
|
||||||
|
for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(float); ++pp) {
|
||||||
|
ioprintf(io, "%f ", *((float *)(prop->mData + pp * sizeof(float))));
|
||||||
|
}
|
||||||
|
} else if (prop->mType == aiPTI_Integer) {
|
||||||
|
ioprintf(io, " size=\"%i\">\n\t\t\t\t",
|
||||||
|
static_cast<int>(prop->mDataLength / sizeof(int)));
|
||||||
|
|
||||||
|
for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(int); ++pp) {
|
||||||
|
ioprintf(io, "%i ", *((int *)(prop->mData + pp * sizeof(int))));
|
||||||
|
}
|
||||||
|
} else if (prop->mType == aiPTI_Buffer) {
|
||||||
|
ioprintf(io, " size=\"%i\">\n\t\t\t\t",
|
||||||
|
static_cast<int>(prop->mDataLength));
|
||||||
|
|
||||||
|
for (unsigned int pp = 0; pp < prop->mDataLength; ++pp) {
|
||||||
|
ioprintf(io, "%2x ", prop->mData[pp]);
|
||||||
|
if (pp && 0 == pp % 30) {
|
||||||
|
ioprintf(io, "\n\t\t\t\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prop->mType == aiPTI_String) {
|
||||||
|
ioprintf(io, ">\n\t\t\t\t\"%s\"", encodeXML(prop->mData + 4).c_str() /* skip length */);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\n\t\t\t</MatProperty>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</MatPropertyList>\n");
|
||||||
|
ioprintf(io, "\t</Material>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "</MaterialList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write animations
|
||||||
|
if (scene->mNumAnimations) {
|
||||||
|
ioprintf(io, "<AnimationList num=\"%u\">\n", scene->mNumAnimations);
|
||||||
|
for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
|
||||||
|
aiAnimation *anim = scene->mAnimations[i];
|
||||||
|
|
||||||
|
// anim header
|
||||||
|
ConvertName(name, anim->mName);
|
||||||
|
ioprintf(io, "\t<Animation name=\"%s\" duration=\"%e\" tick_cnt=\"%e\">\n",
|
||||||
|
name.data, anim->mDuration, anim->mTicksPerSecond);
|
||||||
|
|
||||||
|
// write bone animation channels
|
||||||
|
if (anim->mNumChannels) {
|
||||||
|
ioprintf(io, "\t\t<NodeAnimList num=\"%u\">\n", anim->mNumChannels);
|
||||||
|
for (unsigned int n = 0; n < anim->mNumChannels; ++n) {
|
||||||
|
aiNodeAnim *nd = anim->mChannels[n];
|
||||||
|
|
||||||
|
// node anim header
|
||||||
|
ConvertName(name, nd->mNodeName);
|
||||||
|
ioprintf(io, "\t\t\t<NodeAnim node=\"%s\">\n", name.data);
|
||||||
|
|
||||||
|
if (!shortened) {
|
||||||
|
// write position keys
|
||||||
|
if (nd->mNumPositionKeys) {
|
||||||
|
ioprintf(io, "\t\t\t\t<PositionKeyList num=\"%u\">\n", nd->mNumPositionKeys);
|
||||||
|
for (unsigned int a = 0; a < nd->mNumPositionKeys; ++a) {
|
||||||
|
aiVectorKey *vc = nd->mPositionKeys + a;
|
||||||
|
ioprintf(io, "\t\t\t\t\t<PositionKey time=\"%e\">\n"
|
||||||
|
"\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</PositionKey>\n",
|
||||||
|
vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t\t</PositionKeyList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write scaling keys
|
||||||
|
if (nd->mNumScalingKeys) {
|
||||||
|
ioprintf(io, "\t\t\t\t<ScalingKeyList num=\"%u\">\n", nd->mNumScalingKeys);
|
||||||
|
for (unsigned int a = 0; a < nd->mNumScalingKeys; ++a) {
|
||||||
|
aiVectorKey *vc = nd->mScalingKeys + a;
|
||||||
|
ioprintf(io, "\t\t\t\t\t<ScalingKey time=\"%e\">\n"
|
||||||
|
"\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</ScalingKey>\n",
|
||||||
|
vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t\t</ScalingKeyList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write rotation keys
|
||||||
|
if (nd->mNumRotationKeys) {
|
||||||
|
ioprintf(io, "\t\t\t\t<RotationKeyList num=\"%u\">\n", nd->mNumRotationKeys);
|
||||||
|
for (unsigned int a = 0; a < nd->mNumRotationKeys; ++a) {
|
||||||
|
aiQuatKey *vc = nd->mRotationKeys + a;
|
||||||
|
ioprintf(io, "\t\t\t\t\t<RotationKey time=\"%e\">\n"
|
||||||
|
"\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t</RotationKey>\n",
|
||||||
|
vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z, vc->mValue.w);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t\t</RotationKeyList>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t</NodeAnim>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</NodeAnimList>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t</Animation>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "</AnimationList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write meshes
|
||||||
|
if (scene->mNumMeshes) {
|
||||||
|
ioprintf(io, "<MeshList num=\"%u\">\n", scene->mNumMeshes);
|
||||||
|
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
|
||||||
|
aiMesh *mesh = scene->mMeshes[i];
|
||||||
|
// const unsigned int width = (unsigned int)std::log10((double)mesh->mNumVertices)+1;
|
||||||
|
|
||||||
|
// mesh header
|
||||||
|
ioprintf(io, "\t<Mesh types=\"%s %s %s %s\" material_index=\"%u\">\n",
|
||||||
|
(mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""),
|
||||||
|
(mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""),
|
||||||
|
(mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""),
|
||||||
|
(mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""),
|
||||||
|
mesh->mMaterialIndex);
|
||||||
|
|
||||||
|
// bones
|
||||||
|
if (mesh->mNumBones) {
|
||||||
|
ioprintf(io, "\t\t<BoneList num=\"%u\">\n", mesh->mNumBones);
|
||||||
|
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumBones; ++n) {
|
||||||
|
aiBone *bone = mesh->mBones[n];
|
||||||
|
|
||||||
|
ConvertName(name, bone->mName);
|
||||||
|
// bone header
|
||||||
|
ioprintf(io, "\t\t\t<Bone name=\"%s\">\n"
|
||||||
|
"\t\t\t\t<Matrix4> \n"
|
||||||
|
"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
|
||||||
|
"\t\t\t\t</Matrix4> \n",
|
||||||
|
name.data,
|
||||||
|
bone->mOffsetMatrix.a1, bone->mOffsetMatrix.a2, bone->mOffsetMatrix.a3, bone->mOffsetMatrix.a4,
|
||||||
|
bone->mOffsetMatrix.b1, bone->mOffsetMatrix.b2, bone->mOffsetMatrix.b3, bone->mOffsetMatrix.b4,
|
||||||
|
bone->mOffsetMatrix.c1, bone->mOffsetMatrix.c2, bone->mOffsetMatrix.c3, bone->mOffsetMatrix.c4,
|
||||||
|
bone->mOffsetMatrix.d1, bone->mOffsetMatrix.d2, bone->mOffsetMatrix.d3, bone->mOffsetMatrix.d4);
|
||||||
|
|
||||||
|
if (!shortened && bone->mNumWeights) {
|
||||||
|
ioprintf(io, "\t\t\t\t<WeightList num=\"%u\">\n", bone->mNumWeights);
|
||||||
|
|
||||||
|
// bone weights
|
||||||
|
for (unsigned int a = 0; a < bone->mNumWeights; ++a) {
|
||||||
|
aiVertexWeight *wght = bone->mWeights + a;
|
||||||
|
|
||||||
|
ioprintf(io, "\t\t\t\t\t<Weight index=\"%u\">\n\t\t\t\t\t\t%f\n\t\t\t\t\t</Weight>\n",
|
||||||
|
wght->mVertexId, wght->mWeight);
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t\t</WeightList>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t\t</Bone>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</BoneList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// faces
|
||||||
|
if (!shortened && mesh->mNumFaces) {
|
||||||
|
ioprintf(io, "\t\t<FaceList num=\"%u\">\n", mesh->mNumFaces);
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumFaces; ++n) {
|
||||||
|
aiFace &f = mesh->mFaces[n];
|
||||||
|
ioprintf(io, "\t\t\t<Face num=\"%u\">\n"
|
||||||
|
"\t\t\t\t",
|
||||||
|
f.mNumIndices);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < f.mNumIndices; ++j)
|
||||||
|
ioprintf(io, "%u ", f.mIndices[j]);
|
||||||
|
|
||||||
|
ioprintf(io, "\n\t\t\t</Face>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</FaceList>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertex positions
|
||||||
|
if (mesh->HasPositions()) {
|
||||||
|
ioprintf(io, "\t\t<Positions num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mVertices[n].x,
|
||||||
|
mesh->mVertices[n].y,
|
||||||
|
mesh->mVertices[n].z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Positions>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertex normals
|
||||||
|
if (mesh->HasNormals()) {
|
||||||
|
ioprintf(io, "\t\t<Normals num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mNormals[n].x,
|
||||||
|
mesh->mNormals[n].y,
|
||||||
|
mesh->mNormals[n].z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Normals>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertex tangents and bitangents
|
||||||
|
if (mesh->HasTangentsAndBitangents()) {
|
||||||
|
ioprintf(io, "\t\t<Tangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mTangents[n].x,
|
||||||
|
mesh->mTangents[n].y,
|
||||||
|
mesh->mTangents[n].z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Tangents>\n");
|
||||||
|
|
||||||
|
ioprintf(io, "\t\t<Bitangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mBitangents[n].x,
|
||||||
|
mesh->mBitangents[n].y,
|
||||||
|
mesh->mBitangents[n].z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Bitangents>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// texture coordinates
|
||||||
|
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
|
||||||
|
if (!mesh->mTextureCoords[a])
|
||||||
|
break;
|
||||||
|
|
||||||
|
ioprintf(io, "\t\t<TextureCoords num=\"%u\" set=\"%u\" num_components=\"%u\"> \n", mesh->mNumVertices,
|
||||||
|
a, mesh->mNumUVComponents[a]);
|
||||||
|
|
||||||
|
if (!shortened) {
|
||||||
|
if (mesh->mNumUVComponents[a] == 3) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mTextureCoords[a][n].x,
|
||||||
|
mesh->mTextureCoords[a][n].y,
|
||||||
|
mesh->mTextureCoords[a][n].z);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f\n",
|
||||||
|
mesh->mTextureCoords[a][n].x,
|
||||||
|
mesh->mTextureCoords[a][n].y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</TextureCoords>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertex colors
|
||||||
|
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
|
||||||
|
if (!mesh->mColors[a])
|
||||||
|
break;
|
||||||
|
ioprintf(io, "\t\t<Colors num=\"%u\" set=\"%u\" num_components=\"4\"> \n", mesh->mNumVertices, a);
|
||||||
|
if (!shortened) {
|
||||||
|
for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
|
||||||
|
ioprintf(io, "\t\t%0 8f %0 8f %0 8f %0 8f\n",
|
||||||
|
mesh->mColors[a][n].r,
|
||||||
|
mesh->mColors[a][n].g,
|
||||||
|
mesh->mColors[a][n].b,
|
||||||
|
mesh->mColors[a][n].a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t\t</Colors>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "\t</Mesh>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "</MeshList>\n");
|
||||||
|
}
|
||||||
|
ioprintf(io, "</Scene>\n</ASSIMP>");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace AssxmlFileWriter
|
||||||
|
|
||||||
|
void DumpSceneToAssxml(
|
||||||
|
const char *pFile, const char *cmd, IOSystem *pIOSystem,
|
||||||
|
const aiScene *pScene, bool shortened) {
|
||||||
|
std::unique_ptr<IOStream> file(pIOSystem->Open(pFile, "wt"));
|
||||||
|
if (!file.get()) {
|
||||||
|
throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
AssxmlFileWriter::WriteDump(pFile, cmd, pScene, file.get(), shortened);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace Assimp
|
|
@ -0,0 +1,744 @@
|
||||||
|
/*
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
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 B3DImporter.cpp
|
||||||
|
* @brief Implementation of the b3d importer class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
|
||||||
|
|
||||||
|
// internal headers
|
||||||
|
#include "AssetLib/B3D/B3DImporter.h"
|
||||||
|
#include "PostProcessing/ConvertToLHProcess.h"
|
||||||
|
#include "PostProcessing/TextureTransform.h"
|
||||||
|
|
||||||
|
#include <assimp/StringUtils.h>
|
||||||
|
#include <assimp/anim.h>
|
||||||
|
#include <assimp/importerdesc.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static const aiImporterDesc desc = {
|
||||||
|
"BlitzBasic 3D Importer",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"http://www.blitzbasic.com/",
|
||||||
|
aiImporterFlags_SupportBinaryFlavour,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"b3d"
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4018)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define DEBUG_B3D
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void DeleteAllBarePointers(std::vector<T> &x) {
|
||||||
|
for (auto p : x) {
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
B3DImporter::~B3DImporter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
|
||||||
|
|
||||||
|
size_t pos = pFile.find_last_of('.');
|
||||||
|
if (pos == string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ext = pFile.substr(pos + 1);
|
||||||
|
if (ext.size() != 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ext[0] == 'b' || ext[0] == 'B') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Loader meta information
|
||||||
|
const aiImporterDesc *B3DImporter::GetInfo() const {
|
||||||
|
return &desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
||||||
|
|
||||||
|
// Check whether we can read from the file
|
||||||
|
if (file.get() == nullptr) {
|
||||||
|
throw DeadlyImportError("Failed to open B3D file " + pFile + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether the .b3d file is large enough to contain
|
||||||
|
// at least one chunk.
|
||||||
|
size_t fileSize = file->FileSize();
|
||||||
|
if (fileSize < 8) {
|
||||||
|
throw DeadlyImportError("B3D File is too small.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_pos = 0;
|
||||||
|
_buf.resize(fileSize);
|
||||||
|
file->Read(&_buf[0], 1, fileSize);
|
||||||
|
_stack.clear();
|
||||||
|
|
||||||
|
ReadBB3D(pScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
AI_WONT_RETURN void B3DImporter::Oops() {
|
||||||
|
throw DeadlyImportError("B3D Importer - INTERNAL ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
AI_WONT_RETURN void B3DImporter::Fail(string str) {
|
||||||
|
#ifdef DEBUG_B3D
|
||||||
|
ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
|
||||||
|
#endif
|
||||||
|
throw DeadlyImportError("B3D Importer - error in B3D file data: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
int B3DImporter::ReadByte() {
|
||||||
|
if (_pos > _buf.size()) {
|
||||||
|
Fail("EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buf[_pos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
int B3DImporter::ReadInt() {
|
||||||
|
if (_pos + 4 > _buf.size()) {
|
||||||
|
Fail("EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
int n;
|
||||||
|
memcpy(&n, &_buf[_pos], 4);
|
||||||
|
_pos += 4;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
float B3DImporter::ReadFloat() {
|
||||||
|
if (_pos + 4 > _buf.size()) {
|
||||||
|
Fail("EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
float n;
|
||||||
|
memcpy(&n, &_buf[_pos], 4);
|
||||||
|
_pos += 4;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
aiVector2D B3DImporter::ReadVec2() {
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
return aiVector2D(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
aiVector3D B3DImporter::ReadVec3() {
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
float z = ReadFloat();
|
||||||
|
return aiVector3D(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
aiQuaternion B3DImporter::ReadQuat() {
|
||||||
|
// (aramis_acg) Fix to adapt the loader to changed quat orientation
|
||||||
|
float w = -ReadFloat();
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
float z = ReadFloat();
|
||||||
|
return aiQuaternion(w, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
string B3DImporter::ReadString() {
|
||||||
|
if (_pos > _buf.size()) {
|
||||||
|
Fail("EOF");
|
||||||
|
}
|
||||||
|
string str;
|
||||||
|
while (_pos < _buf.size()) {
|
||||||
|
char c = (char)ReadByte();
|
||||||
|
if (!c) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
str += c;
|
||||||
|
}
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
string B3DImporter::ReadChunk() {
|
||||||
|
string tag;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
tag += char(ReadByte());
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_B3D
|
||||||
|
ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
|
||||||
|
#endif
|
||||||
|
unsigned sz = (unsigned)ReadInt();
|
||||||
|
_stack.push_back(_pos + sz);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ExitChunk() {
|
||||||
|
_pos = _stack.back();
|
||||||
|
_stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
size_t B3DImporter::ChunkSize() {
|
||||||
|
return _stack.back() - _pos;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T *B3DImporter::to_array(const vector<T> &v) {
|
||||||
|
if (v.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
T *p = new T[v.size()];
|
||||||
|
for (size_t i = 0; i < v.size(); ++i) {
|
||||||
|
p[i] = v[i];
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
template <class T>
|
||||||
|
T **unique_to_array(vector<std::unique_ptr<T>> &v) {
|
||||||
|
if (v.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
T **p = new T *[v.size()];
|
||||||
|
for (size_t i = 0; i < v.size(); ++i) {
|
||||||
|
p[i] = v[i].release();
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadTEXS() {
|
||||||
|
while (ChunkSize()) {
|
||||||
|
string name = ReadString();
|
||||||
|
/*int flags=*/ReadInt();
|
||||||
|
/*int blend=*/ReadInt();
|
||||||
|
/*aiVector2D pos=*/ReadVec2();
|
||||||
|
/*aiVector2D scale=*/ReadVec2();
|
||||||
|
/*float rot=*/ReadFloat();
|
||||||
|
|
||||||
|
_textures.push_back(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadBRUS() {
|
||||||
|
int n_texs = ReadInt();
|
||||||
|
if (n_texs < 0 || n_texs > 8) {
|
||||||
|
Fail("Bad texture count");
|
||||||
|
}
|
||||||
|
while (ChunkSize()) {
|
||||||
|
string name = ReadString();
|
||||||
|
aiVector3D color = ReadVec3();
|
||||||
|
float alpha = ReadFloat();
|
||||||
|
float shiny = ReadFloat();
|
||||||
|
/*int blend=**/ ReadInt();
|
||||||
|
int fx = ReadInt();
|
||||||
|
|
||||||
|
std::unique_ptr<aiMaterial> mat(new aiMaterial);
|
||||||
|
|
||||||
|
// Name
|
||||||
|
aiString ainame(name);
|
||||||
|
mat->AddProperty(&ainame, AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
// Diffuse color
|
||||||
|
mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
|
||||||
|
// Opacity
|
||||||
|
mat->AddProperty(&alpha, 1, AI_MATKEY_OPACITY);
|
||||||
|
|
||||||
|
// Specular color
|
||||||
|
aiColor3D speccolor(shiny, shiny, shiny);
|
||||||
|
mat->AddProperty(&speccolor, 1, AI_MATKEY_COLOR_SPECULAR);
|
||||||
|
|
||||||
|
// Specular power
|
||||||
|
float specpow = shiny * 128;
|
||||||
|
mat->AddProperty(&specpow, 1, AI_MATKEY_SHININESS);
|
||||||
|
|
||||||
|
// Double sided
|
||||||
|
if (fx & 0x10) {
|
||||||
|
int i = 1;
|
||||||
|
mat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Textures
|
||||||
|
for (int i = 0; i < n_texs; ++i) {
|
||||||
|
int texid = ReadInt();
|
||||||
|
if (texid < -1 || (texid >= 0 && texid >= static_cast<int>(_textures.size()))) {
|
||||||
|
Fail("Bad texture id");
|
||||||
|
}
|
||||||
|
if (i == 0 && texid >= 0) {
|
||||||
|
aiString texname(_textures[texid]);
|
||||||
|
mat->AddProperty(&texname, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_materials.emplace_back(std::move(mat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadVRTS() {
|
||||||
|
_vflags = ReadInt();
|
||||||
|
_tcsets = ReadInt();
|
||||||
|
_tcsize = ReadInt();
|
||||||
|
if (_tcsets < 0 || _tcsets > 4 || _tcsize < 0 || _tcsize > 4) {
|
||||||
|
Fail("Bad texcoord data");
|
||||||
|
}
|
||||||
|
|
||||||
|
int sz = 12 + (_vflags & 1 ? 12 : 0) + (_vflags & 2 ? 16 : 0) + (_tcsets * _tcsize * 4);
|
||||||
|
size_t n_verts = ChunkSize() / sz;
|
||||||
|
|
||||||
|
int v0 = static_cast<int>(_vertices.size());
|
||||||
|
_vertices.resize(v0 + n_verts);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < n_verts; ++i) {
|
||||||
|
Vertex &v = _vertices[v0 + i];
|
||||||
|
|
||||||
|
memset(v.bones, 0, sizeof(v.bones));
|
||||||
|
memset(v.weights, 0, sizeof(v.weights));
|
||||||
|
|
||||||
|
v.vertex = ReadVec3();
|
||||||
|
|
||||||
|
if (_vflags & 1) {
|
||||||
|
v.normal = ReadVec3();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_vflags & 2) {
|
||||||
|
ReadQuat(); //skip v 4bytes...
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < _tcsets; ++j) {
|
||||||
|
float t[4] = { 0, 0, 0, 0 };
|
||||||
|
for (int k = 0; k < _tcsize; ++k) {
|
||||||
|
t[k] = ReadFloat();
|
||||||
|
}
|
||||||
|
t[1] = 1 - t[1];
|
||||||
|
if (!j) {
|
||||||
|
v.texcoords = aiVector3D(t[0], t[1], t[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadTRIS(int v0) {
|
||||||
|
int matid = ReadInt();
|
||||||
|
if (matid == -1) {
|
||||||
|
matid = 0;
|
||||||
|
} else if (matid < 0 || matid >= (int)_materials.size()) {
|
||||||
|
#ifdef DEBUG_B3D
|
||||||
|
ASSIMP_LOG_ERROR_F("material id=", matid);
|
||||||
|
#endif
|
||||||
|
Fail("Bad material id");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<aiMesh> mesh(new aiMesh);
|
||||||
|
|
||||||
|
mesh->mMaterialIndex = matid;
|
||||||
|
mesh->mNumFaces = 0;
|
||||||
|
mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
||||||
|
|
||||||
|
size_t n_tris = ChunkSize() / 12;
|
||||||
|
aiFace *face = mesh->mFaces = new aiFace[n_tris];
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < n_tris; ++i) {
|
||||||
|
int i0 = ReadInt() + v0;
|
||||||
|
int i1 = ReadInt() + v0;
|
||||||
|
int i2 = ReadInt() + v0;
|
||||||
|
if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
|
||||||
|
#ifdef DEBUG_B3D
|
||||||
|
ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
|
||||||
|
#endif
|
||||||
|
Fail("Bad triangle index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
face->mNumIndices = 3;
|
||||||
|
face->mIndices = new unsigned[3];
|
||||||
|
face->mIndices[0] = i0;
|
||||||
|
face->mIndices[1] = i1;
|
||||||
|
face->mIndices[2] = i2;
|
||||||
|
++mesh->mNumFaces;
|
||||||
|
++face;
|
||||||
|
}
|
||||||
|
|
||||||
|
_meshes.emplace_back(std::move(mesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadMESH() {
|
||||||
|
/*int matid=*/ReadInt();
|
||||||
|
|
||||||
|
int v0 = static_cast<int>(_vertices.size());
|
||||||
|
|
||||||
|
while (ChunkSize()) {
|
||||||
|
string t = ReadChunk();
|
||||||
|
if (t == "VRTS") {
|
||||||
|
ReadVRTS();
|
||||||
|
} else if (t == "TRIS") {
|
||||||
|
ReadTRIS(v0);
|
||||||
|
}
|
||||||
|
ExitChunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadBONE(int id) {
|
||||||
|
while (ChunkSize()) {
|
||||||
|
int vertex = ReadInt();
|
||||||
|
float weight = ReadFloat();
|
||||||
|
if (vertex < 0 || vertex >= (int)_vertices.size()) {
|
||||||
|
Fail("Bad vertex index");
|
||||||
|
}
|
||||||
|
|
||||||
|
Vertex &v = _vertices[vertex];
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!v.weights[i]) {
|
||||||
|
v.bones[i] = static_cast<unsigned char>(id);
|
||||||
|
v.weights[i] = weight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
|
||||||
|
vector<aiVectorKey> trans, scale;
|
||||||
|
vector<aiQuatKey> rot;
|
||||||
|
int flags = ReadInt();
|
||||||
|
while (ChunkSize()) {
|
||||||
|
int frame = ReadInt();
|
||||||
|
if (flags & 1) {
|
||||||
|
trans.push_back(aiVectorKey(frame, ReadVec3()));
|
||||||
|
}
|
||||||
|
if (flags & 2) {
|
||||||
|
scale.push_back(aiVectorKey(frame, ReadVec3()));
|
||||||
|
}
|
||||||
|
if (flags & 4) {
|
||||||
|
rot.push_back(aiQuatKey(frame, ReadQuat()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & 1) {
|
||||||
|
nodeAnim->mNumPositionKeys = static_cast<unsigned int>(trans.size());
|
||||||
|
nodeAnim->mPositionKeys = to_array(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & 2) {
|
||||||
|
nodeAnim->mNumScalingKeys = static_cast<unsigned int>(scale.size());
|
||||||
|
nodeAnim->mScalingKeys = to_array(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & 4) {
|
||||||
|
nodeAnim->mNumRotationKeys = static_cast<unsigned int>(rot.size());
|
||||||
|
nodeAnim->mRotationKeys = to_array(rot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadANIM() {
|
||||||
|
/*int flags=*/ReadInt();
|
||||||
|
int frames = ReadInt();
|
||||||
|
float fps = ReadFloat();
|
||||||
|
|
||||||
|
std::unique_ptr<aiAnimation> anim(new aiAnimation);
|
||||||
|
|
||||||
|
anim->mDuration = frames;
|
||||||
|
anim->mTicksPerSecond = fps;
|
||||||
|
_animations.emplace_back(std::move(anim));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
aiNode *B3DImporter::ReadNODE(aiNode *parent) {
|
||||||
|
|
||||||
|
string name = ReadString();
|
||||||
|
aiVector3D t = ReadVec3();
|
||||||
|
aiVector3D s = ReadVec3();
|
||||||
|
aiQuaternion r = ReadQuat();
|
||||||
|
|
||||||
|
aiMatrix4x4 trans, scale, rot;
|
||||||
|
|
||||||
|
aiMatrix4x4::Translation(t, trans);
|
||||||
|
aiMatrix4x4::Scaling(s, scale);
|
||||||
|
rot = aiMatrix4x4(r.GetMatrix());
|
||||||
|
|
||||||
|
aiMatrix4x4 tform = trans * rot * scale;
|
||||||
|
|
||||||
|
int nodeid = static_cast<int>(_nodes.size());
|
||||||
|
|
||||||
|
aiNode *node = new aiNode(name);
|
||||||
|
_nodes.push_back(node);
|
||||||
|
|
||||||
|
node->mParent = parent;
|
||||||
|
node->mTransformation = tform;
|
||||||
|
|
||||||
|
std::unique_ptr<aiNodeAnim> nodeAnim;
|
||||||
|
vector<unsigned> meshes;
|
||||||
|
vector<aiNode *> children;
|
||||||
|
|
||||||
|
while (ChunkSize()) {
|
||||||
|
const string chunk = ReadChunk();
|
||||||
|
if (chunk == "MESH") {
|
||||||
|
unsigned int n = static_cast<unsigned int>(_meshes.size());
|
||||||
|
ReadMESH();
|
||||||
|
for (unsigned int i = n; i < static_cast<unsigned int>(_meshes.size()); ++i) {
|
||||||
|
meshes.push_back(i);
|
||||||
|
}
|
||||||
|
} else if (chunk == "BONE") {
|
||||||
|
ReadBONE(nodeid);
|
||||||
|
} else if (chunk == "ANIM") {
|
||||||
|
ReadANIM();
|
||||||
|
} else if (chunk == "KEYS") {
|
||||||
|
if (!nodeAnim) {
|
||||||
|
nodeAnim.reset(new aiNodeAnim);
|
||||||
|
nodeAnim->mNodeName = node->mName;
|
||||||
|
}
|
||||||
|
ReadKEYS(nodeAnim.get());
|
||||||
|
} else if (chunk == "NODE") {
|
||||||
|
aiNode *child = ReadNODE(node);
|
||||||
|
children.push_back(child);
|
||||||
|
}
|
||||||
|
ExitChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeAnim) {
|
||||||
|
_nodeAnims.emplace_back(std::move(nodeAnim));
|
||||||
|
}
|
||||||
|
|
||||||
|
node->mNumMeshes = static_cast<unsigned int>(meshes.size());
|
||||||
|
node->mMeshes = to_array(meshes);
|
||||||
|
|
||||||
|
node->mNumChildren = static_cast<unsigned int>(children.size());
|
||||||
|
node->mChildren = to_array(children);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void B3DImporter::ReadBB3D(aiScene *scene) {
|
||||||
|
|
||||||
|
_textures.clear();
|
||||||
|
|
||||||
|
_materials.clear();
|
||||||
|
|
||||||
|
_vertices.clear();
|
||||||
|
|
||||||
|
_meshes.clear();
|
||||||
|
|
||||||
|
DeleteAllBarePointers(_nodes);
|
||||||
|
_nodes.clear();
|
||||||
|
|
||||||
|
_nodeAnims.clear();
|
||||||
|
|
||||||
|
_animations.clear();
|
||||||
|
|
||||||
|
string t = ReadChunk();
|
||||||
|
if (t == "BB3D") {
|
||||||
|
int version = ReadInt();
|
||||||
|
|
||||||
|
if (!DefaultLogger::isNullLogger()) {
|
||||||
|
char dmp[128];
|
||||||
|
ai_snprintf(dmp, 128, "B3D file format version: %i", version);
|
||||||
|
ASSIMP_LOG_INFO(dmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ChunkSize()) {
|
||||||
|
const string chunk = ReadChunk();
|
||||||
|
if (chunk == "TEXS") {
|
||||||
|
ReadTEXS();
|
||||||
|
} else if (chunk == "BRUS") {
|
||||||
|
ReadBRUS();
|
||||||
|
} else if (chunk == "NODE") {
|
||||||
|
ReadNODE(0);
|
||||||
|
}
|
||||||
|
ExitChunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExitChunk();
|
||||||
|
|
||||||
|
if (!_nodes.size()) {
|
||||||
|
Fail("No nodes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_meshes.size()) {
|
||||||
|
Fail("No meshes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix nodes/meshes/bones
|
||||||
|
for (size_t i = 0; i < _nodes.size(); ++i) {
|
||||||
|
aiNode *node = _nodes[i];
|
||||||
|
|
||||||
|
for (size_t j = 0; j < node->mNumMeshes; ++j) {
|
||||||
|
aiMesh *mesh = _meshes[node->mMeshes[j]].get();
|
||||||
|
|
||||||
|
int n_tris = mesh->mNumFaces;
|
||||||
|
int n_verts = mesh->mNumVertices = n_tris * 3;
|
||||||
|
|
||||||
|
aiVector3D *mv = mesh->mVertices = new aiVector3D[n_verts], *mn = 0, *mc = 0;
|
||||||
|
if (_vflags & 1) {
|
||||||
|
mn = mesh->mNormals = new aiVector3D[n_verts];
|
||||||
|
}
|
||||||
|
if (_tcsets) {
|
||||||
|
mc = mesh->mTextureCoords[0] = new aiVector3D[n_verts];
|
||||||
|
}
|
||||||
|
|
||||||
|
aiFace *face = mesh->mFaces;
|
||||||
|
|
||||||
|
vector<vector<aiVertexWeight>> vweights(_nodes.size());
|
||||||
|
|
||||||
|
for (int vertIdx = 0; vertIdx < n_verts; vertIdx += 3) {
|
||||||
|
for (int faceIndex = 0; faceIndex < 3; ++faceIndex) {
|
||||||
|
Vertex &v = _vertices[face->mIndices[faceIndex]];
|
||||||
|
|
||||||
|
*mv++ = v.vertex;
|
||||||
|
if (mn) *mn++ = v.normal;
|
||||||
|
if (mc) *mc++ = v.texcoords;
|
||||||
|
|
||||||
|
face->mIndices[faceIndex] = vertIdx + faceIndex;
|
||||||
|
|
||||||
|
for (int k = 0; k < 4; ++k) {
|
||||||
|
if (!v.weights[k])
|
||||||
|
break;
|
||||||
|
|
||||||
|
int bone = v.bones[k];
|
||||||
|
float weight = v.weights[k];
|
||||||
|
|
||||||
|
vweights[bone].push_back(aiVertexWeight(vertIdx + faceIndex, weight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++face;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<aiBone *> bones;
|
||||||
|
for (size_t weightIndx = 0; weightIndx < vweights.size(); ++weightIndx) {
|
||||||
|
vector<aiVertexWeight> &weights = vweights[weightIndx];
|
||||||
|
if (!weights.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
aiBone *bone = new aiBone;
|
||||||
|
bones.push_back(bone);
|
||||||
|
|
||||||
|
aiNode *bnode = _nodes[weightIndx];
|
||||||
|
|
||||||
|
bone->mName = bnode->mName;
|
||||||
|
bone->mNumWeights = static_cast<unsigned int>(weights.size());
|
||||||
|
bone->mWeights = to_array(weights);
|
||||||
|
|
||||||
|
aiMatrix4x4 mat = bnode->mTransformation;
|
||||||
|
while (bnode->mParent) {
|
||||||
|
bnode = bnode->mParent;
|
||||||
|
mat = bnode->mTransformation * mat;
|
||||||
|
}
|
||||||
|
bone->mOffsetMatrix = mat.Inverse();
|
||||||
|
}
|
||||||
|
mesh->mNumBones = static_cast<unsigned int>(bones.size());
|
||||||
|
mesh->mBones = to_array(bones);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nodes
|
||||||
|
scene->mRootNode = _nodes[0];
|
||||||
|
_nodes.clear(); // node ownership now belongs to scene
|
||||||
|
|
||||||
|
//material
|
||||||
|
if (!_materials.size()) {
|
||||||
|
_materials.emplace_back(std::unique_ptr<aiMaterial>(new aiMaterial));
|
||||||
|
}
|
||||||
|
scene->mNumMaterials = static_cast<unsigned int>(_materials.size());
|
||||||
|
scene->mMaterials = unique_to_array(_materials);
|
||||||
|
|
||||||
|
//meshes
|
||||||
|
scene->mNumMeshes = static_cast<unsigned int>(_meshes.size());
|
||||||
|
scene->mMeshes = unique_to_array(_meshes);
|
||||||
|
|
||||||
|
//animations
|
||||||
|
if (_animations.size() == 1 && _nodeAnims.size()) {
|
||||||
|
|
||||||
|
aiAnimation *anim = _animations.back().get();
|
||||||
|
anim->mNumChannels = static_cast<unsigned int>(_nodeAnims.size());
|
||||||
|
anim->mChannels = unique_to_array(_nodeAnims);
|
||||||
|
|
||||||
|
scene->mNumAnimations = static_cast<unsigned int>(_animations.size());
|
||||||
|
scene->mAnimations = unique_to_array(_animations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to RH
|
||||||
|
MakeLeftHandedProcess makeleft;
|
||||||
|
makeleft.Execute(scene);
|
||||||
|
|
||||||
|
FlipWindingOrderProcess flip;
|
||||||
|
flip.Execute(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER
|
|
@ -0,0 +1,543 @@
|
||||||
|
/** Implementation of the BVH loader */
|
||||||
|
/*
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
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.
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
|
||||||
|
|
||||||
|
#include "BVHLoader.h"
|
||||||
|
#include <assimp/SkeletonMeshBuilder.h>
|
||||||
|
#include <assimp/TinyFormatter.h>
|
||||||
|
#include <assimp/fast_atof.h>
|
||||||
|
#include <assimp/importerdesc.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/IOSystem.hpp>
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
using namespace Assimp::Formatter;
|
||||||
|
|
||||||
|
static const aiImporterDesc desc = {
|
||||||
|
"BVH Importer (MoCap)",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
aiImporterFlags_SupportTextFlavour,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"bvh"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Constructor to be privately used by Importer
|
||||||
|
BVHLoader::BVHLoader() :
|
||||||
|
mLine(),
|
||||||
|
mAnimTickDuration(),
|
||||||
|
mAnimNumFrames(),
|
||||||
|
noSkeletonMesh() {}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Destructor, private as well
|
||||||
|
BVHLoader::~BVHLoader() {}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Returns whether the class can handle the format of the given file.
|
||||||
|
bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const {
|
||||||
|
// check file extension
|
||||||
|
const std::string extension = GetExtension(pFile);
|
||||||
|
|
||||||
|
if (extension == "bvh")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ((!extension.length() || cs) && pIOHandler) {
|
||||||
|
const char *tokens[] = { "HIERARCHY" };
|
||||||
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void BVHLoader::SetupProperties(const Importer *pImp) {
|
||||||
|
noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Loader meta information
|
||||||
|
const aiImporterDesc *BVHLoader::GetInfo() const {
|
||||||
|
return &desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Imports the given file into the given scene structure.
|
||||||
|
void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
|
mFileName = pFile;
|
||||||
|
|
||||||
|
// read file into memory
|
||||||
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
||||||
|
if (file.get() == nullptr) {
|
||||||
|
throw DeadlyImportError("Failed to open file " + pFile + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fileSize = file->FileSize();
|
||||||
|
if (fileSize == 0) {
|
||||||
|
throw DeadlyImportError("File is too small.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuffer.resize(fileSize);
|
||||||
|
file->Read(&mBuffer.front(), 1, fileSize);
|
||||||
|
|
||||||
|
// start reading
|
||||||
|
mReader = mBuffer.begin();
|
||||||
|
mLine = 1;
|
||||||
|
ReadStructure(pScene);
|
||||||
|
|
||||||
|
if (!noSkeletonMesh) {
|
||||||
|
// build a dummy mesh for the skeleton so that we see something at least
|
||||||
|
SkeletonMeshBuilder meshBuilder(pScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct an animation from all the motion data we read
|
||||||
|
CreateAnimation(pScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads the file
|
||||||
|
void BVHLoader::ReadStructure(aiScene *pScene) {
|
||||||
|
// first comes hierarchy
|
||||||
|
std::string header = GetNextToken();
|
||||||
|
if (header != "HIERARCHY")
|
||||||
|
ThrowException("Expected header string \"HIERARCHY\".");
|
||||||
|
ReadHierarchy(pScene);
|
||||||
|
|
||||||
|
// then comes the motion data
|
||||||
|
std::string motion = GetNextToken();
|
||||||
|
if (motion != "MOTION")
|
||||||
|
ThrowException("Expected beginning of motion data \"MOTION\".");
|
||||||
|
ReadMotion(pScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads the hierarchy
|
||||||
|
void BVHLoader::ReadHierarchy(aiScene *pScene) {
|
||||||
|
std::string root = GetNextToken();
|
||||||
|
if (root != "ROOT")
|
||||||
|
ThrowException("Expected root node \"ROOT\".");
|
||||||
|
|
||||||
|
// Go read the hierarchy from here
|
||||||
|
pScene->mRootNode = ReadNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads a node and recursively its childs and returns the created node;
|
||||||
|
aiNode *BVHLoader::ReadNode() {
|
||||||
|
// first token is name
|
||||||
|
std::string nodeName = GetNextToken();
|
||||||
|
if (nodeName.empty() || nodeName == "{")
|
||||||
|
ThrowException(format() << "Expected node name, but found \"" << nodeName << "\".");
|
||||||
|
|
||||||
|
// then an opening brace should follow
|
||||||
|
std::string openBrace = GetNextToken();
|
||||||
|
if (openBrace != "{")
|
||||||
|
ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
|
||||||
|
|
||||||
|
// Create a node
|
||||||
|
aiNode *node = new aiNode(nodeName);
|
||||||
|
std::vector<aiNode *> childNodes;
|
||||||
|
|
||||||
|
// and create an bone entry for it
|
||||||
|
mNodes.push_back(Node(node));
|
||||||
|
Node &internNode = mNodes.back();
|
||||||
|
|
||||||
|
// now read the node's contents
|
||||||
|
std::string siteToken;
|
||||||
|
while (1) {
|
||||||
|
std::string token = GetNextToken();
|
||||||
|
|
||||||
|
// node offset to parent node
|
||||||
|
if (token == "OFFSET")
|
||||||
|
ReadNodeOffset(node);
|
||||||
|
else if (token == "CHANNELS")
|
||||||
|
ReadNodeChannels(internNode);
|
||||||
|
else if (token == "JOINT") {
|
||||||
|
// child node follows
|
||||||
|
aiNode *child = ReadNode();
|
||||||
|
child->mParent = node;
|
||||||
|
childNodes.push_back(child);
|
||||||
|
} else if (token == "End") {
|
||||||
|
// The real symbol is "End Site". Second part comes in a separate token
|
||||||
|
siteToken.clear();
|
||||||
|
siteToken = GetNextToken();
|
||||||
|
if (siteToken != "Site")
|
||||||
|
ThrowException(format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\".");
|
||||||
|
|
||||||
|
aiNode *child = ReadEndSite(nodeName);
|
||||||
|
child->mParent = node;
|
||||||
|
childNodes.push_back(child);
|
||||||
|
} else if (token == "}") {
|
||||||
|
// we're done with that part of the hierarchy
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// everything else is a parse error
|
||||||
|
ThrowException(format() << "Unknown keyword \"" << token << "\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the child nodes if there are any
|
||||||
|
if (childNodes.size() > 0) {
|
||||||
|
node->mNumChildren = static_cast<unsigned int>(childNodes.size());
|
||||||
|
node->mChildren = new aiNode *[node->mNumChildren];
|
||||||
|
std::copy(childNodes.begin(), childNodes.end(), node->mChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and return the sub-hierarchy we built here
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads an end node and returns the created node.
|
||||||
|
aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
|
||||||
|
// check opening brace
|
||||||
|
std::string openBrace = GetNextToken();
|
||||||
|
if (openBrace != "{")
|
||||||
|
ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
|
||||||
|
|
||||||
|
// Create a node
|
||||||
|
aiNode *node = new aiNode("EndSite_" + pParentName);
|
||||||
|
|
||||||
|
// now read the node's contents. Only possible entry is "OFFSET"
|
||||||
|
std::string token;
|
||||||
|
while (1) {
|
||||||
|
token.clear();
|
||||||
|
token = GetNextToken();
|
||||||
|
|
||||||
|
// end node's offset
|
||||||
|
if (token == "OFFSET") {
|
||||||
|
ReadNodeOffset(node);
|
||||||
|
} else if (token == "}") {
|
||||||
|
// we're done with the end node
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// everything else is a parse error
|
||||||
|
ThrowException(format() << "Unknown keyword \"" << token << "\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and return the sub-hierarchy we built here
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads a node offset for the given node
|
||||||
|
void BVHLoader::ReadNodeOffset(aiNode *pNode) {
|
||||||
|
// Offset consists of three floats to read
|
||||||
|
aiVector3D offset;
|
||||||
|
offset.x = GetNextTokenAsFloat();
|
||||||
|
offset.y = GetNextTokenAsFloat();
|
||||||
|
offset.z = GetNextTokenAsFloat();
|
||||||
|
|
||||||
|
// build a transformation matrix from it
|
||||||
|
pNode->mTransformation = aiMatrix4x4(1.0f, 0.0f, 0.0f, offset.x,
|
||||||
|
0.0f, 1.0f, 0.0f, offset.y,
|
||||||
|
0.0f, 0.0f, 1.0f, offset.z,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads the animation channels for the given node
|
||||||
|
void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
|
||||||
|
// number of channels. Use the float reader because we're lazy
|
||||||
|
float numChannelsFloat = GetNextTokenAsFloat();
|
||||||
|
unsigned int numChannels = (unsigned int)numChannelsFloat;
|
||||||
|
|
||||||
|
for (unsigned int a = 0; a < numChannels; a++) {
|
||||||
|
std::string channelToken = GetNextToken();
|
||||||
|
|
||||||
|
if (channelToken == "Xposition")
|
||||||
|
pNode.mChannels.push_back(Channel_PositionX);
|
||||||
|
else if (channelToken == "Yposition")
|
||||||
|
pNode.mChannels.push_back(Channel_PositionY);
|
||||||
|
else if (channelToken == "Zposition")
|
||||||
|
pNode.mChannels.push_back(Channel_PositionZ);
|
||||||
|
else if (channelToken == "Xrotation")
|
||||||
|
pNode.mChannels.push_back(Channel_RotationX);
|
||||||
|
else if (channelToken == "Yrotation")
|
||||||
|
pNode.mChannels.push_back(Channel_RotationY);
|
||||||
|
else if (channelToken == "Zrotation")
|
||||||
|
pNode.mChannels.push_back(Channel_RotationZ);
|
||||||
|
else
|
||||||
|
ThrowException(format() << "Invalid channel specifier \"" << channelToken << "\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads the motion data
|
||||||
|
void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
|
||||||
|
// Read number of frames
|
||||||
|
std::string tokenFrames = GetNextToken();
|
||||||
|
if (tokenFrames != "Frames:")
|
||||||
|
ThrowException(format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
|
||||||
|
|
||||||
|
float numFramesFloat = GetNextTokenAsFloat();
|
||||||
|
mAnimNumFrames = (unsigned int)numFramesFloat;
|
||||||
|
|
||||||
|
// Read frame duration
|
||||||
|
std::string tokenDuration1 = GetNextToken();
|
||||||
|
std::string tokenDuration2 = GetNextToken();
|
||||||
|
if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
|
||||||
|
ThrowException(format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\".");
|
||||||
|
|
||||||
|
mAnimTickDuration = GetNextTokenAsFloat();
|
||||||
|
|
||||||
|
// resize value vectors for each node
|
||||||
|
for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
|
||||||
|
it->mChannelValues.reserve(it->mChannels.size() * mAnimNumFrames);
|
||||||
|
|
||||||
|
// now read all the data and store it in the corresponding node's value vector
|
||||||
|
for (unsigned int frame = 0; frame < mAnimNumFrames; ++frame) {
|
||||||
|
// on each line read the values for all nodes
|
||||||
|
for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) {
|
||||||
|
// get as many values as the node has channels
|
||||||
|
for (unsigned int c = 0; c < it->mChannels.size(); ++c)
|
||||||
|
it->mChannelValues.push_back(GetNextTokenAsFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
// after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Retrieves the next token
|
||||||
|
std::string BVHLoader::GetNextToken() {
|
||||||
|
// skip any preceding whitespace
|
||||||
|
while (mReader != mBuffer.end()) {
|
||||||
|
if (!isspace(*mReader))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// count lines
|
||||||
|
if (*mReader == '\n')
|
||||||
|
mLine++;
|
||||||
|
|
||||||
|
++mReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect all chars till the next whitespace. BVH is easy in respect to that.
|
||||||
|
std::string token;
|
||||||
|
while (mReader != mBuffer.end()) {
|
||||||
|
if (isspace(*mReader))
|
||||||
|
break;
|
||||||
|
|
||||||
|
token.push_back(*mReader);
|
||||||
|
++mReader;
|
||||||
|
|
||||||
|
// little extra logic to make sure braces are counted correctly
|
||||||
|
if (token == "{" || token == "}")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty token means end of file, which is just fine
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Reads the next token as a float
|
||||||
|
float BVHLoader::GetNextTokenAsFloat() {
|
||||||
|
std::string token = GetNextToken();
|
||||||
|
if (token.empty())
|
||||||
|
ThrowException("Unexpected end of file while trying to read a float");
|
||||||
|
|
||||||
|
// check if the float is valid by testing if the atof() function consumed every char of the token
|
||||||
|
const char *ctoken = token.c_str();
|
||||||
|
float result = 0.0f;
|
||||||
|
ctoken = fast_atoreal_move<float>(ctoken, result);
|
||||||
|
|
||||||
|
if (ctoken != token.c_str() + token.length())
|
||||||
|
ThrowException(format() << "Expected a floating point number, but found \"" << token << "\".");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Aborts the file reading with an exception
|
||||||
|
AI_WONT_RETURN void BVHLoader::ThrowException(const std::string &pError) {
|
||||||
|
throw DeadlyImportError(format() << mFileName << ":" << mLine << " - " << pError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Constructs an animation for the motion data and stores it in the given scene
|
||||||
|
void BVHLoader::CreateAnimation(aiScene *pScene) {
|
||||||
|
// create the animation
|
||||||
|
pScene->mNumAnimations = 1;
|
||||||
|
pScene->mAnimations = new aiAnimation *[1];
|
||||||
|
aiAnimation *anim = new aiAnimation;
|
||||||
|
pScene->mAnimations[0] = anim;
|
||||||
|
|
||||||
|
// put down the basic parameters
|
||||||
|
anim->mName.Set("Motion");
|
||||||
|
anim->mTicksPerSecond = 1.0 / double(mAnimTickDuration);
|
||||||
|
anim->mDuration = double(mAnimNumFrames - 1);
|
||||||
|
|
||||||
|
// now generate the tracks for all nodes
|
||||||
|
anim->mNumChannels = static_cast<unsigned int>(mNodes.size());
|
||||||
|
anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
|
||||||
|
|
||||||
|
// FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
|
||||||
|
for (unsigned int i = 0; i < anim->mNumChannels; ++i)
|
||||||
|
anim->mChannels[i] = NULL;
|
||||||
|
|
||||||
|
for (unsigned int a = 0; a < anim->mNumChannels; a++) {
|
||||||
|
const Node &node = mNodes[a];
|
||||||
|
const std::string nodeName = std::string(node.mNode->mName.data);
|
||||||
|
aiNodeAnim *nodeAnim = new aiNodeAnim;
|
||||||
|
anim->mChannels[a] = nodeAnim;
|
||||||
|
nodeAnim->mNodeName.Set(nodeName);
|
||||||
|
std::map<BVHLoader::ChannelType, int> channelMap;
|
||||||
|
|
||||||
|
//Build map of channels
|
||||||
|
for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel) {
|
||||||
|
channelMap[node.mChannels[channel]] = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translational part, if given
|
||||||
|
if (node.mChannels.size() == 6) {
|
||||||
|
nodeAnim->mNumPositionKeys = mAnimNumFrames;
|
||||||
|
nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
|
||||||
|
aiVectorKey *poskey = nodeAnim->mPositionKeys;
|
||||||
|
for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
|
||||||
|
poskey->mTime = double(fr);
|
||||||
|
|
||||||
|
// Now compute all translations
|
||||||
|
for (BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel + 1)) {
|
||||||
|
//Find channel in node
|
||||||
|
std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
|
||||||
|
|
||||||
|
if (mapIter == channelMap.end())
|
||||||
|
throw DeadlyImportError("Missing position channel in node " + nodeName);
|
||||||
|
else {
|
||||||
|
int channelIdx = mapIter->second;
|
||||||
|
switch (channel) {
|
||||||
|
case Channel_PositionX:
|
||||||
|
poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
|
||||||
|
break;
|
||||||
|
case Channel_PositionY:
|
||||||
|
poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
|
||||||
|
break;
|
||||||
|
case Channel_PositionZ:
|
||||||
|
poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++poskey;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if no translation part is given, put a default sequence
|
||||||
|
aiVector3D nodePos(node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
|
||||||
|
nodeAnim->mNumPositionKeys = 1;
|
||||||
|
nodeAnim->mPositionKeys = new aiVectorKey[1];
|
||||||
|
nodeAnim->mPositionKeys[0].mTime = 0.0;
|
||||||
|
nodeAnim->mPositionKeys[0].mValue = nodePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotation part. Always present. First find value offsets
|
||||||
|
{
|
||||||
|
|
||||||
|
// Then create the number of rotation keys
|
||||||
|
nodeAnim->mNumRotationKeys = mAnimNumFrames;
|
||||||
|
nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
|
||||||
|
aiQuatKey *rotkey = nodeAnim->mRotationKeys;
|
||||||
|
for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
|
||||||
|
aiMatrix4x4 temp;
|
||||||
|
aiMatrix3x3 rotMatrix;
|
||||||
|
for (BVHLoader::ChannelType channel = Channel_RotationX; channel <= Channel_RotationZ; channel = (BVHLoader::ChannelType)(channel + 1)) {
|
||||||
|
//Find channel in node
|
||||||
|
std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
|
||||||
|
|
||||||
|
if (mapIter == channelMap.end())
|
||||||
|
throw DeadlyImportError("Missing rotation channel in node " + nodeName);
|
||||||
|
else {
|
||||||
|
int channelIdx = mapIter->second;
|
||||||
|
// translate ZXY euler angels into a quaternion
|
||||||
|
const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
|
||||||
|
|
||||||
|
// Compute rotation transformations in the right order
|
||||||
|
switch (channel) {
|
||||||
|
case Channel_RotationX:
|
||||||
|
aiMatrix4x4::RotationX(angle, temp);
|
||||||
|
rotMatrix *= aiMatrix3x3(temp);
|
||||||
|
break;
|
||||||
|
case Channel_RotationY:
|
||||||
|
aiMatrix4x4::RotationY(angle, temp);
|
||||||
|
rotMatrix *= aiMatrix3x3(temp);
|
||||||
|
break;
|
||||||
|
case Channel_RotationZ:
|
||||||
|
aiMatrix4x4::RotationZ(angle, temp);
|
||||||
|
rotMatrix *= aiMatrix3x3(temp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rotkey->mTime = double(fr);
|
||||||
|
rotkey->mValue = aiQuaternion(rotMatrix);
|
||||||
|
++rotkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scaling part. Always just a default track
|
||||||
|
{
|
||||||
|
nodeAnim->mNumScalingKeys = 1;
|
||||||
|
nodeAnim->mScalingKeys = new aiVectorKey[1];
|
||||||
|
nodeAnim->mScalingKeys[0].mTime = 0.0;
|
||||||
|
nodeAnim->mScalingKeys[0].mValue.Set(1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER
|
|
@ -53,8 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
struct aiNode;
|
struct aiNode;
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
/** Loader class to read Motion Capturing data from a .bvh file.
|
/** Loader class to read Motion Capturing data from a .bvh file.
|
||||||
|
@ -63,12 +62,10 @@ namespace Assimp
|
||||||
* the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
|
* the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
|
||||||
* inside the loader just to be able to see something.
|
* inside the loader just to be able to see something.
|
||||||
*/
|
*/
|
||||||
class BVHLoader : public BaseImporter
|
class BVHLoader : public BaseImporter {
|
||||||
{
|
|
||||||
|
|
||||||
/** Possible animation channels for which the motion data holds the values */
|
/** Possible animation channels for which the motion data holds the values */
|
||||||
enum ChannelType
|
enum ChannelType {
|
||||||
{
|
|
||||||
Channel_PositionX,
|
Channel_PositionX,
|
||||||
Channel_PositionY,
|
Channel_PositionY,
|
||||||
Channel_PositionZ,
|
Channel_PositionZ,
|
||||||
|
@ -78,61 +75,57 @@ class BVHLoader : public BaseImporter
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
|
/** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
|
||||||
struct Node
|
struct Node {
|
||||||
{
|
const aiNode *mNode;
|
||||||
const aiNode* mNode;
|
|
||||||
std::vector<ChannelType> mChannels;
|
std::vector<ChannelType> mChannels;
|
||||||
std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
|
std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
|
||||||
|
|
||||||
Node()
|
Node() :
|
||||||
: mNode(nullptr)
|
mNode(nullptr) {}
|
||||||
{ }
|
|
||||||
|
|
||||||
explicit Node( const aiNode* pNode) : mNode( pNode) { }
|
explicit Node(const aiNode *pNode) :
|
||||||
|
mNode(pNode) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BVHLoader();
|
BVHLoader();
|
||||||
~BVHLoader();
|
~BVHLoader();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* See BaseImporter::CanRead() for details. */
|
||||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const;
|
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const;
|
||||||
|
|
||||||
void SetupProperties(const Importer* pImp);
|
void SetupProperties(const Importer *pImp);
|
||||||
const aiImporterDesc* GetInfo () const;
|
const aiImporterDesc *GetInfo() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
* See BaseImporter::InternReadFile() for details
|
* See BaseImporter::InternReadFile() for details
|
||||||
*/
|
*/
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
|
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Reads the file */
|
/** Reads the file */
|
||||||
void ReadStructure( aiScene* pScene);
|
void ReadStructure(aiScene *pScene);
|
||||||
|
|
||||||
/** Reads the hierarchy */
|
/** Reads the hierarchy */
|
||||||
void ReadHierarchy( aiScene* pScene);
|
void ReadHierarchy(aiScene *pScene);
|
||||||
|
|
||||||
/** Reads a node and recursively its childs and returns the created node. */
|
/** Reads a node and recursively its childs and returns the created node. */
|
||||||
aiNode* ReadNode();
|
aiNode *ReadNode();
|
||||||
|
|
||||||
/** Reads an end node and returns the created node. */
|
/** Reads an end node and returns the created node. */
|
||||||
aiNode* ReadEndSite( const std::string& pParentName);
|
aiNode *ReadEndSite(const std::string &pParentName);
|
||||||
|
|
||||||
/** Reads a node offset for the given node */
|
/** Reads a node offset for the given node */
|
||||||
void ReadNodeOffset( aiNode* pNode);
|
void ReadNodeOffset(aiNode *pNode);
|
||||||
|
|
||||||
/** Reads the animation channels into the given node */
|
/** Reads the animation channels into the given node */
|
||||||
void ReadNodeChannels( BVHLoader::Node& pNode);
|
void ReadNodeChannels(BVHLoader::Node &pNode);
|
||||||
|
|
||||||
/** Reads the motion data */
|
/** Reads the motion data */
|
||||||
void ReadMotion( aiScene* pScene);
|
void ReadMotion(aiScene *pScene);
|
||||||
|
|
||||||
/** Retrieves the next token */
|
/** Retrieves the next token */
|
||||||
std::string GetNextToken();
|
std::string GetNextToken();
|
||||||
|
@ -141,10 +134,10 @@ protected:
|
||||||
float GetNextTokenAsFloat();
|
float GetNextTokenAsFloat();
|
||||||
|
|
||||||
/** Aborts the file reading with an exception */
|
/** Aborts the file reading with an exception */
|
||||||
AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX;
|
AI_WONT_RETURN void ThrowException(const std::string &pError) AI_WONT_RETURN_SUFFIX;
|
||||||
|
|
||||||
/** Constructs an animation for the motion data and stores it in the given scene */
|
/** Constructs an animation for the motion data and stores it in the given scene */
|
||||||
void CreateAnimation( aiScene* pScene);
|
void CreateAnimation(aiScene *pScene);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Filename, for a verbose error message */
|
/** Filename, for a verbose error message */
|
|
@ -44,137 +44,117 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||||
|
|
||||||
|
#include "BlenderBMesh.h"
|
||||||
#include "BlenderDNA.h"
|
#include "BlenderDNA.h"
|
||||||
#include "BlenderScene.h"
|
#include "BlenderScene.h"
|
||||||
#include "BlenderBMesh.h"
|
|
||||||
#include "BlenderTessellator.h"
|
#include "BlenderTessellator.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
template <>
|
||||||
template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix()
|
const char *LogFunctions<BlenderBMeshConverter>::Prefix() {
|
||||||
{
|
static auto prefix = "BLEND_BMESH: ";
|
||||||
static auto prefix = "BLEND_BMESH: ";
|
return prefix;
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace Assimp
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
using namespace Assimp::Blender;
|
using namespace Assimp::Blender;
|
||||||
using namespace Assimp::Formatter;
|
using namespace Assimp::Formatter;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
|
BlenderBMeshConverter::BlenderBMeshConverter(const Mesh *mesh) :
|
||||||
BMesh( mesh ),
|
BMesh(mesh),
|
||||||
triMesh( NULL )
|
triMesh(nullptr) {
|
||||||
{
|
ai_assert(nullptr != mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
BlenderBMeshConverter::~BlenderBMeshConverter( )
|
BlenderBMeshConverter::~BlenderBMeshConverter() {
|
||||||
{
|
DestroyTriMesh();
|
||||||
DestroyTriMesh( );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool BlenderBMeshConverter::ContainsBMesh( ) const
|
bool BlenderBMeshConverter::ContainsBMesh() const {
|
||||||
{
|
|
||||||
// TODO - Should probably do some additional verification here
|
// TODO - Should probably do some additional verification here
|
||||||
return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
|
return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
|
const Mesh *BlenderBMeshConverter::TriangulateBMesh() {
|
||||||
{
|
AssertValidMesh();
|
||||||
AssertValidMesh( );
|
AssertValidSizes();
|
||||||
AssertValidSizes( );
|
PrepareTriMesh();
|
||||||
PrepareTriMesh( );
|
|
||||||
|
|
||||||
for ( int i = 0; i < BMesh->totpoly; ++i )
|
for (int i = 0; i < BMesh->totpoly; ++i) {
|
||||||
{
|
const MPoly &poly = BMesh->mpoly[i];
|
||||||
const MPoly& poly = BMesh->mpoly[ i ];
|
ConvertPolyToFaces(poly);
|
||||||
ConvertPolyToFaces( poly );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return triMesh;
|
return triMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::AssertValidMesh( )
|
void BlenderBMeshConverter::AssertValidMesh() {
|
||||||
{
|
if (!ContainsBMesh()) {
|
||||||
if ( !ContainsBMesh( ) )
|
ThrowException("BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first");
|
||||||
{
|
|
||||||
ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::AssertValidSizes( )
|
void BlenderBMeshConverter::AssertValidSizes() {
|
||||||
{
|
if (BMesh->totpoly != static_cast<int>(BMesh->mpoly.size())) {
|
||||||
if ( BMesh->totpoly != static_cast<int>( BMesh->mpoly.size( ) ) )
|
ThrowException("BMesh poly array has incorrect size");
|
||||||
{
|
|
||||||
ThrowException( "BMesh poly array has incorrect size" );
|
|
||||||
}
|
}
|
||||||
if ( BMesh->totloop != static_cast<int>( BMesh->mloop.size( ) ) )
|
if (BMesh->totloop != static_cast<int>(BMesh->mloop.size())) {
|
||||||
{
|
ThrowException("BMesh loop array has incorrect size");
|
||||||
ThrowException( "BMesh loop array has incorrect size" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::PrepareTriMesh( )
|
void BlenderBMeshConverter::PrepareTriMesh() {
|
||||||
{
|
if (triMesh) {
|
||||||
if ( triMesh )
|
DestroyTriMesh();
|
||||||
{
|
|
||||||
DestroyTriMesh( );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
triMesh = new Mesh( *BMesh );
|
triMesh = new Mesh(*BMesh);
|
||||||
triMesh->totface = 0;
|
triMesh->totface = 0;
|
||||||
triMesh->mface.clear( );
|
triMesh->mface.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::DestroyTriMesh( )
|
void BlenderBMeshConverter::DestroyTriMesh() {
|
||||||
{
|
|
||||||
delete triMesh;
|
delete triMesh;
|
||||||
triMesh = NULL;
|
triMesh = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
|
void BlenderBMeshConverter::ConvertPolyToFaces(const MPoly &poly) {
|
||||||
{
|
const MLoop *polyLoop = &BMesh->mloop[poly.loopstart];
|
||||||
const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
|
|
||||||
|
|
||||||
if ( poly.totloop == 3 || poly.totloop == 4 )
|
if (poly.totloop == 3 || poly.totloop == 4) {
|
||||||
{
|
AddFace(polyLoop[0].v, polyLoop[1].v, polyLoop[2].v, poly.totloop == 4 ? polyLoop[3].v : 0);
|
||||||
AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
|
|
||||||
|
|
||||||
// UVs are optional, so only convert when present.
|
// UVs are optional, so only convert when present.
|
||||||
if ( BMesh->mloopuv.size() )
|
if (BMesh->mloopuv.size()) {
|
||||||
{
|
if ((poly.loopstart + poly.totloop) > static_cast<int>(BMesh->mloopuv.size())) {
|
||||||
if ( (poly.loopstart + poly.totloop ) > static_cast<int>( BMesh->mloopuv.size() ) )
|
ThrowException("BMesh uv loop array has incorrect size");
|
||||||
{
|
|
||||||
ThrowException( "BMesh uv loop array has incorrect size" );
|
|
||||||
}
|
}
|
||||||
const MLoopUV* loopUV = &BMesh->mloopuv[ poly.loopstart ];
|
const MLoopUV *loopUV = &BMesh->mloopuv[poly.loopstart];
|
||||||
AddTFace( loopUV[ 0 ].uv, loopUV[ 1 ].uv, loopUV[ 2 ].uv, poly.totloop == 4 ? loopUV[ 3 ].uv : 0 );
|
AddTFace(loopUV[0].uv, loopUV[1].uv, loopUV[2].uv, poly.totloop == 4 ? loopUV[3].uv : 0);
|
||||||
}
|
}
|
||||||
}
|
} else if (poly.totloop > 4) {
|
||||||
else if ( poly.totloop > 4 )
|
|
||||||
{
|
|
||||||
#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
|
#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
|
||||||
BlenderTessellatorGL tessGL( *this );
|
BlenderTessellatorGL tessGL(*this);
|
||||||
tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
|
tessGL.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
|
||||||
#elif ASSIMP_BLEND_WITH_POLY_2_TRI
|
#elif ASSIMP_BLEND_WITH_POLY_2_TRI
|
||||||
BlenderTessellatorP2T tessP2T( *this );
|
BlenderTessellatorP2T tessP2T(*this);
|
||||||
tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
|
tessP2T.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
|
void BlenderBMeshConverter::AddFace(int v1, int v2, int v3, int v4) {
|
||||||
{
|
|
||||||
MFace face;
|
MFace face;
|
||||||
face.v1 = v1;
|
face.v1 = v1;
|
||||||
face.v2 = v2;
|
face.v2 = v2;
|
||||||
|
@ -183,24 +163,22 @@ void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
|
||||||
face.flag = 0;
|
face.flag = 0;
|
||||||
// TODO - Work out how materials work
|
// TODO - Work out how materials work
|
||||||
face.mat_nr = 0;
|
face.mat_nr = 0;
|
||||||
triMesh->mface.push_back( face );
|
triMesh->mface.push_back(face);
|
||||||
triMesh->totface = static_cast<int>(triMesh->mface.size( ));
|
triMesh->totface = static_cast<int>(triMesh->mface.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void BlenderBMeshConverter::AddTFace( const float* uv1, const float *uv2, const float *uv3, const float* uv4 )
|
void BlenderBMeshConverter::AddTFace(const float *uv1, const float *uv2, const float *uv3, const float *uv4) {
|
||||||
{
|
|
||||||
MTFace mtface;
|
MTFace mtface;
|
||||||
memcpy( &mtface.uv[ 0 ], uv1, sizeof(float) * 2 );
|
memcpy(&mtface.uv[0], uv1, sizeof(float) * 2);
|
||||||
memcpy( &mtface.uv[ 1 ], uv2, sizeof(float) * 2 );
|
memcpy(&mtface.uv[1], uv2, sizeof(float) * 2);
|
||||||
memcpy( &mtface.uv[ 2 ], uv3, sizeof(float) * 2 );
|
memcpy(&mtface.uv[2], uv3, sizeof(float) * 2);
|
||||||
|
|
||||||
if ( uv4 )
|
if (uv4) {
|
||||||
{
|
memcpy(&mtface.uv[3], uv4, sizeof(float) * 2);
|
||||||
memcpy( &mtface.uv[ 3 ], uv4, sizeof(float) * 2 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
triMesh->mtface.push_back( mtface );
|
triMesh->mtface.push_back(mtface);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
|
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
|
|
@ -0,0 +1,181 @@
|
||||||
|
#include "BlenderCustomData.h"
|
||||||
|
#include "BlenderDNA.h"
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
namespace Blender {
|
||||||
|
/**
|
||||||
|
* @brief read/convert of Structure array to memory
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
bool read(const Structure &s, T *p, const size_t cnt, const FileDatabase &db) {
|
||||||
|
for (size_t i = 0; i < cnt; ++i) {
|
||||||
|
T read;
|
||||||
|
s.Convert(read, db);
|
||||||
|
*p = read;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief pointer to function read memory for n CustomData types
|
||||||
|
*/
|
||||||
|
typedef bool (*PRead)(ElemBase *pOut, const size_t cnt, const FileDatabase &db);
|
||||||
|
typedef ElemBase *(*PCreate)(const size_t cnt);
|
||||||
|
typedef void (*PDestroy)(ElemBase *);
|
||||||
|
|
||||||
|
#define IMPL_STRUCT_READ(ty) \
|
||||||
|
bool read##ty(ElemBase *v, const size_t cnt, const FileDatabase &db) { \
|
||||||
|
ty *ptr = dynamic_cast<ty *>(v); \
|
||||||
|
if (nullptr == ptr) { \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
return read<ty>(db.dna[#ty], ptr, cnt, db); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IMPL_STRUCT_CREATE(ty) \
|
||||||
|
ElemBase *create##ty(const size_t cnt) { \
|
||||||
|
return new ty[cnt]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IMPL_STRUCT_DESTROY(ty) \
|
||||||
|
void destroy##ty(ElemBase *pE) { \
|
||||||
|
ty *p = dynamic_cast<ty *>(pE); \
|
||||||
|
delete[] p; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief helper macro to define Structure functions
|
||||||
|
*/
|
||||||
|
#define IMPL_STRUCT(ty) \
|
||||||
|
IMPL_STRUCT_READ(ty) \
|
||||||
|
IMPL_STRUCT_CREATE(ty) \
|
||||||
|
IMPL_STRUCT_DESTROY(ty)
|
||||||
|
|
||||||
|
// supported structures for CustomData
|
||||||
|
IMPL_STRUCT(MVert)
|
||||||
|
IMPL_STRUCT(MEdge)
|
||||||
|
IMPL_STRUCT(MFace)
|
||||||
|
IMPL_STRUCT(MTFace)
|
||||||
|
IMPL_STRUCT(MTexPoly)
|
||||||
|
IMPL_STRUCT(MLoopUV)
|
||||||
|
IMPL_STRUCT(MLoopCol)
|
||||||
|
IMPL_STRUCT(MPoly)
|
||||||
|
IMPL_STRUCT(MLoop)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief describes the size of data and the read function to be used for single CustomerData.type
|
||||||
|
*/
|
||||||
|
struct CustomDataTypeDescription {
|
||||||
|
PRead Read; ///< function to read one CustomData type element
|
||||||
|
PCreate Create; ///< function to allocate n type elements
|
||||||
|
PDestroy Destroy;
|
||||||
|
|
||||||
|
CustomDataTypeDescription(PRead read, PCreate create, PDestroy destroy) :
|
||||||
|
Read(read), Create(create), Destroy(destroy) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief helper macro to define Structure type specific CustomDataTypeDescription
|
||||||
|
* @note IMPL_STRUCT_READ for same ty must be used earlier to implement the typespecific read function
|
||||||
|
*/
|
||||||
|
#define DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(ty) \
|
||||||
|
CustomDataTypeDescription { &read##ty, &create##ty, &destroy##ty }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief helper macro to define CustomDataTypeDescription for UNSUPPORTED type
|
||||||
|
*/
|
||||||
|
#define DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION \
|
||||||
|
CustomDataTypeDescription { nullptr, nullptr, nullptr }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief descriptors for data pointed to from CustomDataLayer.data
|
||||||
|
* @note some of the CustomData uses already well defined Structures
|
||||||
|
* other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
|
||||||
|
* use a special readfunction for that cases
|
||||||
|
*/
|
||||||
|
std::array<CustomDataTypeDescription, CD_NUMTYPES> customDataTypeDescriptions = { { DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MFace),
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTFace),
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTexPoly),
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopUV),
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopCol),
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MPoly),
|
||||||
|
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoop),
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
|
||||||
|
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION } };
|
||||||
|
|
||||||
|
bool isValidCustomDataType(const int cdtype) {
|
||||||
|
return cdtype >= 0 && cdtype < CD_NUMTYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
|
||||||
|
if (!isValidCustomDataType(cdtype)) {
|
||||||
|
throw Error((Formatter::format(), "CustomData.type ", cdtype, " out of index"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];
|
||||||
|
if (cdtd.Read && cdtd.Create && cdtd.Destroy && cnt > 0) {
|
||||||
|
// allocate cnt elements and parse them from file
|
||||||
|
out.reset(cdtd.Create(cnt), cdtd.Destroy);
|
||||||
|
return cdtd.Read(out.get(), cnt, db);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
|
||||||
|
for (auto it = customdata.layers.begin(); it != customdata.layers.end(); ++it) {
|
||||||
|
if (it->get()->type == cdtype && name == it->get()->name) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElemBase *getCustomDataLayerData(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
|
||||||
|
const std::shared_ptr<CustomDataLayer> pLayer = getCustomDataLayer(customdata, cdtype, name);
|
||||||
|
if (pLayer && pLayer->data) {
|
||||||
|
return pLayer->data.get();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace Blender
|
||||||
|
} // namespace Assimp
|
|
@ -45,25 +45,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* serialized set of data structures.
|
* serialized set of data structures.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||||
#include "BlenderDNA.h"
|
#include "BlenderDNA.h"
|
||||||
#include <assimp/StreamReader.h>
|
#include <assimp/StreamReader.h>
|
||||||
#include <assimp/fast_atof.h>
|
|
||||||
#include <assimp/TinyFormatter.h>
|
#include <assimp/TinyFormatter.h>
|
||||||
|
#include <assimp/fast_atof.h>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
using namespace Assimp::Blender;
|
using namespace Assimp::Blender;
|
||||||
using namespace Assimp::Formatter;
|
using namespace Assimp::Formatter;
|
||||||
|
|
||||||
static bool match4(StreamReaderAny& stream, const char* string) {
|
static bool match4(StreamReaderAny &stream, const char *string) {
|
||||||
ai_assert( nullptr != string );
|
ai_assert(nullptr != string);
|
||||||
char tmp[4];
|
char tmp[4];
|
||||||
tmp[ 0 ] = ( stream ).GetI1();
|
tmp[0] = (stream).GetI1();
|
||||||
tmp[ 1 ] = ( stream ).GetI1();
|
tmp[1] = (stream).GetI1();
|
||||||
tmp[ 2 ] = ( stream ).GetI1();
|
tmp[2] = (stream).GetI1();
|
||||||
tmp[ 3 ] = ( stream ).GetI1();
|
tmp[3] = (stream).GetI1();
|
||||||
return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
|
return (tmp[0] == string[0] && tmp[1] == string[1] && tmp[2] == string[2] && tmp[3] == string[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
|
@ -72,75 +71,76 @@ struct Type {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void DNAParser::Parse ()
|
void DNAParser::Parse() {
|
||||||
{
|
StreamReaderAny &stream = *db.reader.get();
|
||||||
StreamReaderAny& stream = *db.reader.get();
|
DNA &dna = db.dna;
|
||||||
DNA& dna = db.dna;
|
|
||||||
|
|
||||||
if(!match4(stream,"SDNA")) {
|
if (!match4(stream, "SDNA")) {
|
||||||
throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
|
throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// name dictionary
|
// name dictionary
|
||||||
if(!match4(stream,"NAME")) {
|
if (!match4(stream, "NAME")) {
|
||||||
throw DeadlyImportError("BlenderDNA: Expected NAME field");
|
throw DeadlyImportError("BlenderDNA: Expected NAME field");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> names (stream.GetI4());
|
std::vector<std::string> names(stream.GetI4());
|
||||||
for(std::string& s : names) {
|
for (std::string &s : names) {
|
||||||
while (char c = stream.GetI1()) {
|
while (char c = stream.GetI1()) {
|
||||||
s += c;
|
s += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type dictionary
|
// type dictionary
|
||||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||||
if(!match4(stream,"TYPE")) {
|
;
|
||||||
|
if (!match4(stream, "TYPE")) {
|
||||||
throw DeadlyImportError("BlenderDNA: Expected TYPE field");
|
throw DeadlyImportError("BlenderDNA: Expected TYPE field");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Type> types (stream.GetI4());
|
std::vector<Type> types(stream.GetI4());
|
||||||
for(Type& s : types) {
|
for (Type &s : types) {
|
||||||
while (char c = stream.GetI1()) {
|
while (char c = stream.GetI1()) {
|
||||||
s.name += c;
|
s.name += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type length dictionary
|
// type length dictionary
|
||||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||||
if(!match4(stream,"TLEN")) {
|
;
|
||||||
|
if (!match4(stream, "TLEN")) {
|
||||||
throw DeadlyImportError("BlenderDNA: Expected TLEN field");
|
throw DeadlyImportError("BlenderDNA: Expected TLEN field");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Type& s : types) {
|
for (Type &s : types) {
|
||||||
s.size = stream.GetI2();
|
s.size = stream.GetI2();
|
||||||
}
|
}
|
||||||
|
|
||||||
// structures dictionary
|
// structures dictionary
|
||||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||||
if(!match4(stream,"STRC")) {
|
;
|
||||||
|
if (!match4(stream, "STRC")) {
|
||||||
throw DeadlyImportError("BlenderDNA: Expected STRC field");
|
throw DeadlyImportError("BlenderDNA: Expected STRC field");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t end = stream.GetI4(), fields = 0;
|
size_t end = stream.GetI4(), fields = 0;
|
||||||
|
|
||||||
dna.structures.reserve(end);
|
dna.structures.reserve(end);
|
||||||
for(size_t i = 0; i != end; ++i) {
|
for (size_t i = 0; i != end; ++i) {
|
||||||
|
|
||||||
uint16_t n = stream.GetI2();
|
uint16_t n = stream.GetI2();
|
||||||
if (n >= types.size()) {
|
if (n >= types.size()) {
|
||||||
throw DeadlyImportError((format(),
|
throw DeadlyImportError((format(),
|
||||||
"BlenderDNA: Invalid type index in structure name" ,n,
|
"BlenderDNA: Invalid type index in structure name", n,
|
||||||
" (there are only ", types.size(), " entries)"
|
" (there are only ", types.size(), " entries)"));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// maintain separate indexes
|
// maintain separate indexes
|
||||||
dna.indices[types[n].name] = dna.structures.size();
|
dna.indices[types[n].name] = dna.structures.size();
|
||||||
|
|
||||||
dna.structures.push_back(Structure());
|
dna.structures.push_back(Structure());
|
||||||
Structure& s = dna.structures.back();
|
Structure &s = dna.structures.back();
|
||||||
s.name = types[n].name;
|
s.name = types[n].name;
|
||||||
//s.index = dna.structures.size()-1;
|
//s.index = dna.structures.size()-1;
|
||||||
|
|
||||||
n = stream.GetI2();
|
n = stream.GetI2();
|
||||||
|
@ -152,12 +152,11 @@ void DNAParser::Parse ()
|
||||||
uint16_t j = stream.GetI2();
|
uint16_t j = stream.GetI2();
|
||||||
if (j >= types.size()) {
|
if (j >= types.size()) {
|
||||||
throw DeadlyImportError((format(),
|
throw DeadlyImportError((format(),
|
||||||
"BlenderDNA: Invalid type index in structure field ", j,
|
"BlenderDNA: Invalid type index in structure field ", j,
|
||||||
" (there are only ", types.size(), " entries)"
|
" (there are only ", types.size(), " entries)"));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
s.fields.push_back(Field());
|
s.fields.push_back(Field());
|
||||||
Field& f = s.fields.back();
|
Field &f = s.fields.back();
|
||||||
f.offset = offset;
|
f.offset = offset;
|
||||||
|
|
||||||
f.type = types[j].name;
|
f.type = types[j].name;
|
||||||
|
@ -166,9 +165,8 @@ void DNAParser::Parse ()
|
||||||
j = stream.GetI2();
|
j = stream.GetI2();
|
||||||
if (j >= names.size()) {
|
if (j >= names.size()) {
|
||||||
throw DeadlyImportError((format(),
|
throw DeadlyImportError((format(),
|
||||||
"BlenderDNA: Invalid name index in structure field ", j,
|
"BlenderDNA: Invalid name index in structure field ", j,
|
||||||
" (there are only ", names.size(), " entries)"
|
" (there are only ", names.size(), " entries)"));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.name = names[j];
|
f.name = names[j];
|
||||||
|
@ -191,26 +189,25 @@ void DNAParser::Parse ()
|
||||||
const std::string::size_type rb = f.name.find('[');
|
const std::string::size_type rb = f.name.find('[');
|
||||||
if (rb == std::string::npos) {
|
if (rb == std::string::npos) {
|
||||||
throw DeadlyImportError((format(),
|
throw DeadlyImportError((format(),
|
||||||
"BlenderDNA: Encountered invalid array declaration ",
|
"BlenderDNA: Encountered invalid array declaration ",
|
||||||
f.name
|
f.name));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.flags |= FieldFlag_Array;
|
f.flags |= FieldFlag_Array;
|
||||||
DNA::ExtractArraySize(f.name,f.array_sizes);
|
DNA::ExtractArraySize(f.name, f.array_sizes);
|
||||||
f.name = f.name.substr(0,rb);
|
f.name = f.name.substr(0, rb);
|
||||||
|
|
||||||
f.size *= f.array_sizes[0] * f.array_sizes[1];
|
f.size *= f.array_sizes[0] * f.array_sizes[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// maintain separate indexes
|
// maintain separate indexes
|
||||||
s.indices[f.name] = s.fields.size()-1;
|
s.indices[f.name] = s.fields.size() - 1;
|
||||||
offset += f.size;
|
offset += f.size;
|
||||||
}
|
}
|
||||||
s.size = offset;
|
s.size = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSIMP_LOG_DEBUG_F( "BlenderDNA: Got ", dna.structures.size()," structures with totally ",fields," fields");
|
ASSIMP_LOG_DEBUG_F("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields");
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||||
dna.DumpToFile();
|
dna.DumpToFile();
|
||||||
|
@ -220,13 +217,11 @@ void DNAParser::Parse ()
|
||||||
dna.RegisterConverters();
|
dna.RegisterConverters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void DNA :: DumpToFile()
|
void DNA ::DumpToFile() {
|
||||||
{
|
|
||||||
// we don't bother using the VFS here for this is only for debugging.
|
// we don't bother using the VFS here for this is only for debugging.
|
||||||
// (and all your bases are belong to us).
|
// (and all your bases are belong to us).
|
||||||
|
|
||||||
|
@ -235,12 +230,14 @@ void DNA :: DumpToFile()
|
||||||
ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
|
ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
f << "Field format: type name offset size" << "\n";
|
f << "Field format: type name offset size"
|
||||||
f << "Structure format: name size" << "\n";
|
<< "\n";
|
||||||
|
f << "Structure format: name size"
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
for(const Structure& s : structures) {
|
for (const Structure &s : structures) {
|
||||||
f << s.name << " " << s.size << "\n\n";
|
f << s.name << " " << s.size << "\n\n";
|
||||||
for(const Field& ff : s.fields) {
|
for (const Field &ff : s.fields) {
|
||||||
f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n";
|
f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n";
|
||||||
}
|
}
|
||||||
f << "\n";
|
f << "\n";
|
||||||
|
@ -252,11 +249,9 @@ void DNA :: DumpToFile()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
/*static*/ void DNA :: ExtractArraySize(
|
/*static*/ void DNA ::ExtractArraySize(
|
||||||
const std::string& out,
|
const std::string &out,
|
||||||
size_t array_sizes[2]
|
size_t array_sizes[2]) {
|
||||||
)
|
|
||||||
{
|
|
||||||
array_sizes[0] = array_sizes[1] = 1;
|
array_sizes[0] = array_sizes[1] = 1;
|
||||||
std::string::size_type pos = out.find('[');
|
std::string::size_type pos = out.find('[');
|
||||||
if (pos++ == std::string::npos) {
|
if (pos++ == std::string::npos) {
|
||||||
|
@ -264,7 +259,7 @@ void DNA :: DumpToFile()
|
||||||
}
|
}
|
||||||
array_sizes[0] = strtoul10(&out[pos]);
|
array_sizes[0] = strtoul10(&out[pos]);
|
||||||
|
|
||||||
pos = out.find('[',pos);
|
pos = out.find('[', pos);
|
||||||
if (pos++ == std::string::npos) {
|
if (pos++ == std::string::npos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -272,36 +267,32 @@ void DNA :: DumpToFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
|
std::shared_ptr<ElemBase> DNA ::ConvertBlobToStructure(
|
||||||
const Structure& structure,
|
const Structure &structure,
|
||||||
const FileDatabase& db
|
const FileDatabase &db) const {
|
||||||
) const
|
std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
|
||||||
{
|
|
||||||
std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
|
|
||||||
if (it == converters.end()) {
|
if (it == converters.end()) {
|
||||||
return std::shared_ptr< ElemBase >();
|
return std::shared_ptr<ElemBase>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
|
std::shared_ptr<ElemBase> ret = (structure.*((*it).second.first))();
|
||||||
(structure.*((*it).second.second))(ret,db);
|
(structure.*((*it).second.second))(ret, db);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
DNA::FactoryPair DNA :: GetBlobToStructureConverter(
|
DNA::FactoryPair DNA ::GetBlobToStructureConverter(
|
||||||
const Structure& structure,
|
const Structure &structure,
|
||||||
const FileDatabase& /*db*/
|
const FileDatabase & /*db*/
|
||||||
) const
|
) const {
|
||||||
{
|
std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
|
||||||
std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
|
|
||||||
return it == converters.end() ? FactoryPair() : (*it).second;
|
return it == converters.end() ? FactoryPair() : (*it).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// basing on http://www.blender.org/development/architecture/notes-on-sdna/
|
// basing on http://www.blender.org/development/architecture/notes-on-sdna/
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void DNA :: AddPrimitiveStructures()
|
void DNA ::AddPrimitiveStructures() {
|
||||||
{
|
|
||||||
// NOTE: these are just dummies. Their presence enforces
|
// NOTE: these are just dummies. Their presence enforces
|
||||||
// Structure::Convert<target_type> to be called on these
|
// Structure::Convert<target_type> to be called on these
|
||||||
// empty structures. These converters are special
|
// empty structures. These converters are special
|
||||||
|
@ -311,30 +302,27 @@ void DNA :: AddPrimitiveStructures()
|
||||||
// in question.
|
// in question.
|
||||||
|
|
||||||
indices["int"] = structures.size();
|
indices["int"] = structures.size();
|
||||||
structures.push_back( Structure() );
|
structures.push_back(Structure());
|
||||||
structures.back().name = "int";
|
structures.back().name = "int";
|
||||||
structures.back().size = 4;
|
structures.back().size = 4;
|
||||||
|
|
||||||
indices["short"] = structures.size();
|
indices["short"] = structures.size();
|
||||||
structures.push_back( Structure() );
|
structures.push_back(Structure());
|
||||||
structures.back().name = "short";
|
structures.back().name = "short";
|
||||||
structures.back().size = 2;
|
structures.back().size = 2;
|
||||||
|
|
||||||
|
|
||||||
indices["char"] = structures.size();
|
indices["char"] = structures.size();
|
||||||
structures.push_back( Structure() );
|
structures.push_back(Structure());
|
||||||
structures.back().name = "char";
|
structures.back().name = "char";
|
||||||
structures.back().size = 1;
|
structures.back().size = 1;
|
||||||
|
|
||||||
|
|
||||||
indices["float"] = structures.size();
|
indices["float"] = structures.size();
|
||||||
structures.push_back( Structure() );
|
structures.push_back(Structure());
|
||||||
structures.back().name = "float";
|
structures.back().name = "float";
|
||||||
structures.back().size = 4;
|
structures.back().size = 4;
|
||||||
|
|
||||||
|
|
||||||
indices["double"] = structures.size();
|
indices["double"] = structures.size();
|
||||||
structures.push_back( Structure() );
|
structures.push_back(Structure());
|
||||||
structures.back().name = "double";
|
structures.back().name = "double";
|
||||||
structures.back().size = 8;
|
structures.back().size = 8;
|
||||||
|
|
||||||
|
@ -342,8 +330,7 @@ void DNA :: AddPrimitiveStructures()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void SectionParser :: Next()
|
void SectionParser ::Next() {
|
||||||
{
|
|
||||||
stream.SetCurrentPos(current.start + current.size);
|
stream.SetCurrentPos(current.start + current.size);
|
||||||
|
|
||||||
const char tmp[] = {
|
const char tmp[] = {
|
||||||
|
@ -352,7 +339,7 @@ void SectionParser :: Next()
|
||||||
(const char)stream.GetI1(),
|
(const char)stream.GetI1(),
|
||||||
(const char)stream.GetI1()
|
(const char)stream.GetI1()
|
||||||
};
|
};
|
||||||
current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1);
|
current.id = std::string(tmp, tmp[3] ? 4 : tmp[2] ? 3 : tmp[1] ? 2 : 1);
|
||||||
|
|
||||||
current.size = stream.GetI4();
|
current.size = stream.GetI4();
|
||||||
current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
|
current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
|
||||||
|
@ -370,6 +357,4 @@ void SectionParser :: Next()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -49,26 +49,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <assimp/BaseImporter.h>
|
#include <assimp/BaseImporter.h>
|
||||||
#include <assimp/StreamReader.h>
|
#include <assimp/StreamReader.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <memory>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
// enable verbose log output. really verbose, so be careful.
|
// enable verbose log output. really verbose, so be careful.
|
||||||
#ifdef ASSIMP_BUILD_DEBUG
|
#ifdef ASSIMP_BUILD_DEBUG
|
||||||
# define ASSIMP_BUILD_BLENDER_DEBUG
|
#define ASSIMP_BUILD_BLENDER_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #define ASSIMP_BUILD_BLENDER_NO_STATS
|
// #define ASSIMP_BUILD_BLENDER_NO_STATS
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
template <bool,bool> class StreamReader;
|
template <bool, bool>
|
||||||
typedef StreamReader<true,true> StreamReaderAny;
|
class StreamReader;
|
||||||
|
typedef StreamReader<true, true> StreamReaderAny;
|
||||||
|
|
||||||
namespace Blender {
|
namespace Blender {
|
||||||
|
|
||||||
class FileDatabase;
|
class FileDatabase;
|
||||||
struct FileBlockHead;
|
struct FileBlockHead;
|
||||||
|
|
||||||
template <template <typename> class TOUT>
|
template <template <typename> class TOUT>
|
||||||
|
@ -82,8 +83,8 @@ class ObjectCache;
|
||||||
* ancestry. */
|
* ancestry. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Error : DeadlyImportError {
|
struct Error : DeadlyImportError {
|
||||||
Error (const std::string& s)
|
Error(const std::string &s) :
|
||||||
: DeadlyImportError(s) {
|
DeadlyImportError(s) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -93,9 +94,8 @@ struct Error : DeadlyImportError {
|
||||||
* descendents. It serves as base class for all data structure fields. */
|
* descendents. It serves as base class for all data structure fields. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct ElemBase {
|
struct ElemBase {
|
||||||
ElemBase()
|
ElemBase() :
|
||||||
: dna_type(nullptr)
|
dna_type(nullptr) {
|
||||||
{
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ struct ElemBase {
|
||||||
* data type is not static, i.e. a std::shared_ptr<ElemBase>
|
* data type is not static, i.e. a std::shared_ptr<ElemBase>
|
||||||
* in the scene description would have its type resolved
|
* in the scene description would have its type resolved
|
||||||
* at runtime, so this member is always set. */
|
* at runtime, so this member is always set. */
|
||||||
const char* dna_type;
|
const char *dna_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
|
@ -120,8 +120,8 @@ struct ElemBase {
|
||||||
* they used to point to.*/
|
* they used to point to.*/
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Pointer {
|
struct Pointer {
|
||||||
Pointer()
|
Pointer() :
|
||||||
: val() {
|
val() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
@ -131,8 +131,8 @@ struct Pointer {
|
||||||
/** Represents a generic offset within a BLEND file */
|
/** Represents a generic offset within a BLEND file */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct FileOffset {
|
struct FileOffset {
|
||||||
FileOffset()
|
FileOffset() :
|
||||||
: val() {
|
val() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
@ -154,7 +154,7 @@ public:
|
||||||
resize(0);
|
resize(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool () const {
|
operator bool() const {
|
||||||
return !empty();
|
return !empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -164,7 +164,7 @@ public:
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
enum FieldFlags {
|
enum FieldFlags {
|
||||||
FieldFlag_Pointer = 0x1,
|
FieldFlag_Pointer = 0x1,
|
||||||
FieldFlag_Array = 0x2
|
FieldFlag_Array = 0x2
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
|
@ -200,7 +200,7 @@ enum ErrorPolicy {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||||
# define ErrorPolicy_Igno ErrorPolicy_Warn
|
#define ErrorPolicy_Igno ErrorPolicy_Warn
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
|
@ -212,47 +212,42 @@ enum ErrorPolicy {
|
||||||
* meaningful contents. */
|
* meaningful contents. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
class Structure {
|
class Structure {
|
||||||
template <template <typename> class> friend class ObjectCache;
|
template <template <typename> class>
|
||||||
|
friend class ObjectCache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Structure()
|
Structure() :
|
||||||
: cache_idx(static_cast<size_t>(-1) ){
|
cache_idx(static_cast<size_t>(-1)) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// publicly accessible members
|
// publicly accessible members
|
||||||
std::string name;
|
std::string name;
|
||||||
vector< Field > fields;
|
vector<Field> fields;
|
||||||
std::map<std::string, size_t> indices;
|
std::map<std::string, size_t> indices;
|
||||||
|
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Access a field of the structure by its canonical name. The pointer version
|
/** Access a field of the structure by its canonical name. The pointer version
|
||||||
* returns NULL on failure while the reference version raises an import error. */
|
* returns NULL on failure while the reference version raises an import error. */
|
||||||
inline const Field& operator [] (const std::string& ss) const;
|
inline const Field &operator[](const std::string &ss) const;
|
||||||
inline const Field* Get (const std::string& ss) const;
|
inline const Field *Get(const std::string &ss) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Access a field of the structure by its index */
|
/** Access a field of the structure by its index */
|
||||||
inline const Field& operator [] (const size_t i) const;
|
inline const Field &operator[](const size_t i) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
inline bool operator== (const Structure& other) const {
|
inline bool operator==(const Structure &other) const {
|
||||||
return name == other.name; // name is meant to be an unique identifier
|
return name == other.name; // name is meant to be an unique identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
inline bool operator!= (const Structure& other) const {
|
inline bool operator!=(const Structure &other) const {
|
||||||
return name != other.name;
|
return name != other.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Try to read an instance of the structure from the stream
|
/** Try to read an instance of the structure from the stream
|
||||||
* and attempt to convert to `T`. This is done by
|
* and attempt to convert to `T`. This is done by
|
||||||
|
@ -260,54 +255,54 @@ public:
|
||||||
* a compiler complain is the result.
|
* a compiler complain is the result.
|
||||||
* @param dest Destination value to be written
|
* @param dest Destination value to be written
|
||||||
* @param db File database, including input stream. */
|
* @param db File database, including input stream. */
|
||||||
template <typename T> void Convert (T& dest, const FileDatabase& db) const;
|
template <typename T>
|
||||||
|
void Convert(T &dest, const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// generic converter
|
// generic converter
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Convert(std::shared_ptr<ElemBase> in,const FileDatabase& db) const;
|
void Convert(std::shared_ptr<ElemBase> in, const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// generic allocator
|
// generic allocator
|
||||||
template <typename T> std::shared_ptr<ElemBase> Allocate() const;
|
template <typename T>
|
||||||
|
std::shared_ptr<ElemBase> Allocate() const;
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// field parsing for 1d arrays
|
// field parsing for 1d arrays
|
||||||
template <int error_policy, typename T, size_t M>
|
template <int error_policy, typename T, size_t M>
|
||||||
void ReadFieldArray(T (& out)[M], const char* name,
|
void ReadFieldArray(T (&out)[M], const char *name,
|
||||||
const FileDatabase& db) const;
|
const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// field parsing for 2d arrays
|
// field parsing for 2d arrays
|
||||||
template <int error_policy, typename T, size_t M, size_t N>
|
template <int error_policy, typename T, size_t M, size_t N>
|
||||||
void ReadFieldArray2(T (& out)[M][N], const char* name,
|
void ReadFieldArray2(T (&out)[M][N], const char *name,
|
||||||
const FileDatabase& db) const;
|
const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// field parsing for pointer or dynamic array types
|
// field parsing for pointer or dynamic array types
|
||||||
// (std::shared_ptr)
|
// (std::shared_ptr)
|
||||||
// The return value indicates whether the data was already cached.
|
// The return value indicates whether the data was already cached.
|
||||||
template <int error_policy, template <typename> class TOUT, typename T>
|
template <int error_policy, template <typename> class TOUT, typename T>
|
||||||
bool ReadFieldPtr(TOUT<T>& out, const char* name,
|
bool ReadFieldPtr(TOUT<T> &out, const char *name,
|
||||||
const FileDatabase& db,
|
const FileDatabase &db,
|
||||||
bool non_recursive = false) const;
|
bool non_recursive = false) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// field parsing for static arrays of pointer or dynamic
|
// field parsing for static arrays of pointer or dynamic
|
||||||
// array types (std::shared_ptr[])
|
// array types (std::shared_ptr[])
|
||||||
// The return value indicates whether the data was already cached.
|
// The return value indicates whether the data was already cached.
|
||||||
template <int error_policy, template <typename> class TOUT, typename T, size_t N>
|
template <int error_policy, template <typename> class TOUT, typename T, size_t N>
|
||||||
bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
|
bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name,
|
||||||
const FileDatabase& db) const;
|
const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// field parsing for `normal` values
|
// field parsing for `normal` values
|
||||||
// The return value indicates whether the data was already cached.
|
// The return value indicates whether the data was already cached.
|
||||||
template <int error_policy, typename T>
|
template <int error_policy, typename T>
|
||||||
void ReadField(T& out, const char* name,
|
void ReadField(T &out, const char *name,
|
||||||
const FileDatabase& db) const;
|
const FileDatabase &db) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
|
@ -318,7 +313,7 @@ public:
|
||||||
* @return true when read was successful
|
* @return true when read was successful
|
||||||
*/
|
*/
|
||||||
template <int error_policy, template <typename> class TOUT, typename T>
|
template <int error_policy, template <typename> class TOUT, typename T>
|
||||||
bool ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const;
|
bool ReadFieldPtrVector(vector<TOUT<T>> &out, const char *name, const FileDatabase &db) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief parses raw customdata
|
* @brief parses raw customdata
|
||||||
|
@ -329,40 +324,40 @@ public:
|
||||||
* @return true when read was successful
|
* @return true when read was successful
|
||||||
*/
|
*/
|
||||||
template <int error_policy>
|
template <int error_policy>
|
||||||
bool ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const;
|
bool ReadCustomDataPtr(std::shared_ptr<ElemBase> &out, int cdtype, const char *name, const FileDatabase &db) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// --------------------------------------------------------
|
||||||
|
template <template <typename> class TOUT, typename T>
|
||||||
|
bool ResolvePointer(TOUT<T> &out, const Pointer &ptrval,
|
||||||
|
const FileDatabase &db, const Field &f,
|
||||||
|
bool non_recursive = false) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
template <template <typename> class TOUT, typename T>
|
template <template <typename> class TOUT, typename T>
|
||||||
bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
|
bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval,
|
||||||
const FileDatabase& db, const Field& f,
|
const FileDatabase &db, const Field &f, bool) const;
|
||||||
bool non_recursive = false) const;
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
template <template <typename> class TOUT, typename T>
|
bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval,
|
||||||
bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
|
const FileDatabase &db, const Field &f, bool) const;
|
||||||
const FileDatabase& db, const Field& f, bool) const;
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
bool ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
|
inline const FileBlockHead *LocateFileBlockForAddress(
|
||||||
const FileDatabase& db, const Field& f, bool) const;
|
const Pointer &ptrval,
|
||||||
|
const FileDatabase &db) const;
|
||||||
// --------------------------------------------------------
|
|
||||||
inline const FileBlockHead* LocateFileBlockForAddress(
|
|
||||||
const Pointer & ptrval,
|
|
||||||
const FileDatabase& db) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
template <typename T> T* _allocate(std::shared_ptr<T>& out, size_t& s) const {
|
template <typename T>
|
||||||
|
T *_allocate(std::shared_ptr<T> &out, size_t &s) const {
|
||||||
out = std::shared_ptr<T>(new T());
|
out = std::shared_ptr<T>(new T());
|
||||||
s = 1;
|
s = 1;
|
||||||
return out.get();
|
return out.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
|
template <typename T>
|
||||||
|
T *_allocate(vector<T> &out, size_t &s) const {
|
||||||
out.resize(s);
|
out.resize(s);
|
||||||
return s ? &out.front() : NULL;
|
return s ? &out.front() : NULL;
|
||||||
}
|
}
|
||||||
|
@ -372,14 +367,14 @@ private:
|
||||||
struct _defaultInitializer {
|
struct _defaultInitializer {
|
||||||
|
|
||||||
template <typename T, unsigned int N>
|
template <typename T, unsigned int N>
|
||||||
void operator ()(T (& out)[N], const char* = NULL) {
|
void operator()(T (&out)[N], const char * = NULL) {
|
||||||
for (unsigned int i = 0; i < N; ++i) {
|
for (unsigned int i = 0; i < N; ++i) {
|
||||||
out[i] = T();
|
out[i] = T();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, unsigned int N, unsigned int M>
|
template <typename T, unsigned int N, unsigned int M>
|
||||||
void operator ()(T (& out)[N][M], const char* = NULL) {
|
void operator()(T (&out)[N][M], const char * = NULL) {
|
||||||
for (unsigned int i = 0; i < N; ++i) {
|
for (unsigned int i = 0; i < N; ++i) {
|
||||||
for (unsigned int j = 0; j < M; ++j) {
|
for (unsigned int j = 0; j < M; ++j) {
|
||||||
out[i][j] = T();
|
out[i][j] = T();
|
||||||
|
@ -388,21 +383,21 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void operator ()(T& out, const char* = NULL) {
|
void operator()(T &out, const char * = NULL) {
|
||||||
out = T();
|
out = T();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable size_t cache_idx;
|
mutable size_t cache_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
template <> struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
|
template <>
|
||||||
|
struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void operator ()(T& out, const char* reason = "<add reason>") {
|
void operator()(T &out, const char *reason = "<add reason>") {
|
||||||
ASSIMP_LOG_WARN(reason);
|
ASSIMP_LOG_WARN(reason);
|
||||||
|
|
||||||
// ... and let the show go on
|
// ... and let the show go on
|
||||||
|
@ -410,10 +405,11 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
|
template <>
|
||||||
|
struct Structure ::_defaultInitializer<ErrorPolicy_Fail> {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void operator ()(T& /*out*/,const char* = "") {
|
void operator()(T & /*out*/, const char * = "") {
|
||||||
// obviously, it is crucial that _DefaultInitializer is used
|
// obviously, it is crucial that _DefaultInitializer is used
|
||||||
// only from within a catch clause.
|
// only from within a catch clause.
|
||||||
throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
|
throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
|
||||||
|
@ -421,13 +417,12 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------
|
||||||
template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out,
|
template <>
|
||||||
const Pointer & ptrval,
|
inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out,
|
||||||
const FileDatabase& db,
|
const Pointer &ptrval,
|
||||||
const Field& f,
|
const FileDatabase &db,
|
||||||
bool
|
const Field &f,
|
||||||
) const;
|
bool) const;
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Represents the full data structure information for a single BLEND file.
|
/** Represents the full data structure information for a single BLEND file.
|
||||||
|
@ -435,40 +430,34 @@ template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(st
|
||||||
* #DNAParser does the reading and represents currently the only place where
|
* #DNAParser does the reading and represents currently the only place where
|
||||||
* DNA is altered.*/
|
* DNA is altered.*/
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
class DNA
|
class DNA {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
typedef void (Structure::*ConvertProcPtr)(
|
||||||
typedef void (Structure::*ConvertProcPtr) (
|
std::shared_ptr<ElemBase> in,
|
||||||
std::shared_ptr<ElemBase> in,
|
const FileDatabase &) const;
|
||||||
const FileDatabase&
|
|
||||||
) const;
|
|
||||||
|
|
||||||
typedef std::shared_ptr<ElemBase> (
|
typedef std::shared_ptr<ElemBase> (
|
||||||
Structure::*AllocProcPtr) () const;
|
Structure::*AllocProcPtr)() const;
|
||||||
|
|
||||||
typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
|
typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::map<std::string, FactoryPair> converters;
|
||||||
std::map<std::string, FactoryPair > converters;
|
vector<Structure> structures;
|
||||||
vector<Structure > structures;
|
|
||||||
std::map<std::string, size_t> indices;
|
std::map<std::string, size_t> indices;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Access a structure by its canonical name, the pointer version returns NULL on failure
|
/** Access a structure by its canonical name, the pointer version returns NULL on failure
|
||||||
* while the reference version raises an error. */
|
* while the reference version raises an error. */
|
||||||
inline const Structure& operator [] (const std::string& ss) const;
|
inline const Structure &operator[](const std::string &ss) const;
|
||||||
inline const Structure* Get (const std::string& ss) const;
|
inline const Structure *Get(const std::string &ss) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Access a structure by its index */
|
/** Access a structure by its index */
|
||||||
inline const Structure& operator [] (const size_t i) const;
|
inline const Structure &operator[](const size_t i) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Add structure definitions for all the primitive types,
|
/** Add structure definitions for all the primitive types,
|
||||||
* i.e. integer, short, char, float */
|
* i.e. integer, short, char, float */
|
||||||
|
@ -483,7 +472,6 @@ public:
|
||||||
* known at compile time (consier Object::data).*/
|
* known at compile time (consier Object::data).*/
|
||||||
void RegisterConverters();
|
void RegisterConverters();
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Take an input blob from the stream, interpret it according to
|
/** Take an input blob from the stream, interpret it according to
|
||||||
* a its structure name and convert it to the intermediate
|
* a its structure name and convert it to the intermediate
|
||||||
|
@ -491,10 +479,9 @@ public:
|
||||||
* @param structure Destination structure definition
|
* @param structure Destination structure definition
|
||||||
* @param db File database.
|
* @param db File database.
|
||||||
* @return A null pointer if no appropriate converter is available.*/
|
* @return A null pointer if no appropriate converter is available.*/
|
||||||
std::shared_ptr< ElemBase > ConvertBlobToStructure(
|
std::shared_ptr<ElemBase> ConvertBlobToStructure(
|
||||||
const Structure& structure,
|
const Structure &structure,
|
||||||
const FileDatabase& db
|
const FileDatabase &db) const;
|
||||||
) const;
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Find a suitable conversion function for a given Structure.
|
/** Find a suitable conversion function for a given Structure.
|
||||||
|
@ -505,10 +492,8 @@ public:
|
||||||
* @param db File database.
|
* @param db File database.
|
||||||
* @return A null pointer in .first if no appropriate converter is available.*/
|
* @return A null pointer in .first if no appropriate converter is available.*/
|
||||||
FactoryPair GetBlobToStructureConverter(
|
FactoryPair GetBlobToStructureConverter(
|
||||||
const Structure& structure,
|
const Structure &structure,
|
||||||
const FileDatabase& db
|
const FileDatabase &db) const;
|
||||||
) const;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
@ -527,25 +512,29 @@ public:
|
||||||
* @throw DeadlyImportError if more than 2 dimensions are
|
* @throw DeadlyImportError if more than 2 dimensions are
|
||||||
* encountered. */
|
* encountered. */
|
||||||
static void ExtractArraySize(
|
static void ExtractArraySize(
|
||||||
const std::string& out,
|
const std::string &out,
|
||||||
size_t array_sizes[2]
|
size_t array_sizes[2]);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// special converters for primitive types
|
// special converters for primitive types
|
||||||
template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const;
|
template <>
|
||||||
template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const;
|
inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const;
|
||||||
template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const;
|
template <>
|
||||||
template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const;
|
inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const;
|
||||||
template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const;
|
template <>
|
||||||
template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const;
|
inline void Structure ::Convert<char>(char &dest, const FileDatabase &db) const;
|
||||||
|
template <>
|
||||||
|
inline void Structure ::Convert<float>(float &dest, const FileDatabase &db) const;
|
||||||
|
template <>
|
||||||
|
inline void Structure ::Convert<double>(double &dest, const FileDatabase &db) const;
|
||||||
|
template <>
|
||||||
|
inline void Structure ::Convert<Pointer>(Pointer &dest, const FileDatabase &db) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Describes a master file block header. Each master file sections holds n
|
/** Describes a master file block header. Each master file sections holds n
|
||||||
* elements of a certain SDNA structure (or otherwise unspecified data). */
|
* elements of a certain SDNA structure (or otherwise unspecified data). */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct FileBlockHead
|
struct FileBlockHead {
|
||||||
{
|
|
||||||
// points right after the header of the file block
|
// points right after the header of the file block
|
||||||
StreamReaderAny::pos start;
|
StreamReaderAny::pos start;
|
||||||
|
|
||||||
|
@ -561,66 +550,55 @@ struct FileBlockHead
|
||||||
// number of structure instances to follow
|
// number of structure instances to follow
|
||||||
size_t num;
|
size_t num;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// file blocks are sorted by address to quickly locate specific memory addresses
|
// file blocks are sorted by address to quickly locate specific memory addresses
|
||||||
bool operator < (const FileBlockHead& o) const {
|
bool operator<(const FileBlockHead &o) const {
|
||||||
return address.val < o.address.val;
|
return address.val < o.address.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for std::upper_bound
|
// for std::upper_bound
|
||||||
operator const Pointer& () const {
|
operator const Pointer &() const {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// for std::upper_bound
|
// for std::upper_bound
|
||||||
inline bool operator< (const Pointer& a, const Pointer& b) {
|
inline bool operator<(const Pointer &a, const Pointer &b) {
|
||||||
return a.val < b.val;
|
return a.val < b.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Utility to read all master file blocks in turn. */
|
/** Utility to read all master file blocks in turn. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
class SectionParser
|
class SectionParser {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** @param stream Inout stream, must point to the
|
/** @param stream Inout stream, must point to the
|
||||||
* first section in the file. Call Next() once
|
* first section in the file. Call Next() once
|
||||||
* to have it read.
|
* to have it read.
|
||||||
* @param ptr64 Pointer size in file is 64 bits? */
|
* @param ptr64 Pointer size in file is 64 bits? */
|
||||||
SectionParser(StreamReaderAny& stream,bool ptr64)
|
SectionParser(StreamReaderAny &stream, bool ptr64) :
|
||||||
: stream(stream)
|
stream(stream), ptr64(ptr64) {
|
||||||
, ptr64(ptr64)
|
|
||||||
{
|
|
||||||
current.size = current.start = 0;
|
current.size = current.start = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
const FileBlockHead& GetCurrent() const {
|
const FileBlockHead &GetCurrent() const {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Advance to the next section.
|
/** Advance to the next section.
|
||||||
* @throw DeadlyImportError if the last chunk was passed. */
|
* @throw DeadlyImportError if the last chunk was passed. */
|
||||||
void Next();
|
void Next();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FileBlockHead current;
|
FileBlockHead current;
|
||||||
StreamReaderAny& stream;
|
StreamReaderAny &stream;
|
||||||
bool ptr64;
|
bool ptr64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Import statistics, i.e. number of file blocks read*/
|
/** Import statistics, i.e. number of file blocks read*/
|
||||||
|
@ -628,17 +606,13 @@ public:
|
||||||
class Statistics {
|
class Statistics {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Statistics() :
|
||||||
Statistics ()
|
fields_read(), pointers_resolved(), cache_hits()
|
||||||
: fields_read ()
|
// , blocks_read ()
|
||||||
, pointers_resolved ()
|
,
|
||||||
, cache_hits ()
|
cached_objects() {}
|
||||||
// , blocks_read ()
|
|
||||||
, cached_objects ()
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** total number of fields we read */
|
/** total number of fields we read */
|
||||||
unsigned int fields_read;
|
unsigned int fields_read;
|
||||||
|
|
||||||
|
@ -662,17 +636,13 @@ public:
|
||||||
* avoids circular references and avoids object duplication. */
|
* avoids circular references and avoids object duplication. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
template <template <typename> class TOUT>
|
template <template <typename> class TOUT>
|
||||||
class ObjectCache
|
class ObjectCache {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
typedef std::map<Pointer, TOUT<ElemBase>> StructureCache;
|
||||||
typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ObjectCache(const FileDatabase &db) :
|
||||||
ObjectCache(const FileDatabase& db)
|
db(db) {
|
||||||
: db(db)
|
|
||||||
{
|
|
||||||
// currently there are only ~400 structure records per blend file.
|
// currently there are only ~400 structure records per blend file.
|
||||||
// we read only a small part of them and don't cache objects
|
// we read only a small part of them and don't cache objects
|
||||||
// which we don't need, so this should suffice.
|
// which we don't need, so this should suffice.
|
||||||
|
@ -680,17 +650,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Check whether a specific item is in the cache.
|
/** Check whether a specific item is in the cache.
|
||||||
* @param s Data type of the item
|
* @param s Data type of the item
|
||||||
* @param out Output pointer. Unchanged if the
|
* @param out Output pointer. Unchanged if the
|
||||||
* cache doesn't know the item yet.
|
* cache doesn't know the item yet.
|
||||||
* @param ptr Item address to look for. */
|
* @param ptr Item address to look for. */
|
||||||
template <typename T> void get (
|
template <typename T>
|
||||||
const Structure& s,
|
void get(
|
||||||
TOUT<T>& out,
|
const Structure &s,
|
||||||
const Pointer& ptr) const;
|
TOUT<T> &out,
|
||||||
|
const Pointer &ptr) const;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Add an item to the cache after the item has
|
/** Add an item to the cache after the item has
|
||||||
|
@ -700,47 +670,44 @@ public:
|
||||||
* @param s Data type of the item
|
* @param s Data type of the item
|
||||||
* @param out Item to insert into the cache
|
* @param out Item to insert into the cache
|
||||||
* @param ptr address (cache key) of the item. */
|
* @param ptr address (cache key) of the item. */
|
||||||
template <typename T> void set
|
template <typename T>
|
||||||
(const Structure& s,
|
void set(const Structure &s,
|
||||||
const TOUT<T>& out,
|
const TOUT<T> &out,
|
||||||
const Pointer& ptr);
|
const Pointer &ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable vector<StructureCache> caches;
|
mutable vector<StructureCache> caches;
|
||||||
const FileDatabase& db;
|
const FileDatabase &db;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
template <> class ObjectCache<Blender::vector>
|
template <>
|
||||||
{
|
class ObjectCache<Blender::vector> {
|
||||||
public:
|
public:
|
||||||
|
ObjectCache(const FileDatabase &) {}
|
||||||
|
|
||||||
ObjectCache(const FileDatabase&) {}
|
template <typename T>
|
||||||
|
void get(const Structure &, vector<T> &, const Pointer &) {}
|
||||||
template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
|
template <typename T>
|
||||||
template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
|
void set(const Structure &, const vector<T> &, const Pointer &) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(disable:4355)
|
#pragma warning(disable : 4355)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Memory representation of a full BLEND file and all its dependencies. The
|
/** Memory representation of a full BLEND file and all its dependencies. The
|
||||||
* output aiScene is constructed from an instance of this data structure. */
|
* output aiScene is constructed from an instance of this data structure. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
class FileDatabase
|
class FileDatabase {
|
||||||
{
|
template <template <typename> class TOUT>
|
||||||
template <template <typename> class TOUT> friend class ObjectCache;
|
friend class ObjectCache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileDatabase()
|
FileDatabase() :
|
||||||
: _cacheArrays(*this)
|
_cacheArrays(*this), _cache(*this), next_cache_idx() {}
|
||||||
, _cache(*this)
|
|
||||||
, next_cache_idx()
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// publicly accessible fields
|
// publicly accessible fields
|
||||||
|
@ -748,12 +715,11 @@ public:
|
||||||
bool little;
|
bool little;
|
||||||
|
|
||||||
DNA dna;
|
DNA dna;
|
||||||
std::shared_ptr< StreamReaderAny > reader;
|
std::shared_ptr<StreamReaderAny> reader;
|
||||||
vector< FileBlockHead > entries;
|
vector<FileBlockHead> entries;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Statistics &stats() const {
|
||||||
Statistics& stats() const {
|
|
||||||
return _stats;
|
return _stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,18 +728,16 @@ public:
|
||||||
// arrays of objects are never cached because we can't easily
|
// arrays of objects are never cached because we can't easily
|
||||||
// ensure their proper destruction.
|
// ensure their proper destruction.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ObjectCache<std::shared_ptr>& cache(std::shared_ptr<T>& /*in*/) const {
|
ObjectCache<std::shared_ptr> &cache(std::shared_ptr<T> & /*in*/) const {
|
||||||
return _cache;
|
return _cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ObjectCache<vector>& cache(vector<T>& /*in*/) const {
|
ObjectCache<vector> &cache(vector<T> & /*in*/) const {
|
||||||
return _cacheArrays;
|
return _cacheArrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
||||||
mutable Statistics _stats;
|
mutable Statistics _stats;
|
||||||
#endif
|
#endif
|
||||||
|
@ -785,24 +749,20 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(default:4355)
|
#pragma warning(default : 4355)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
|
/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
class DNAParser
|
class DNAParser {
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Bind the parser to a empty DNA and an input stream */
|
/** Bind the parser to a empty DNA and an input stream */
|
||||||
DNAParser(FileDatabase& db)
|
DNAParser(FileDatabase &db) :
|
||||||
: db(db)
|
db(db) {}
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
/** Locate the DNA in the file and parse it. The input
|
/** Locate the DNA in the file and parse it. The input
|
||||||
* stream is expected to point to the beginning of the DN1
|
* stream is expected to point to the beginning of the DN1
|
||||||
|
@ -811,18 +771,16 @@ public:
|
||||||
* @throw DeadlyImportError if the DNA cannot be read.
|
* @throw DeadlyImportError if the DNA cannot be read.
|
||||||
* @note The position of the stream pointer is undefined
|
* @note The position of the stream pointer is undefined
|
||||||
* afterwards.*/
|
* afterwards.*/
|
||||||
void Parse ();
|
void Parse();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Obtain a reference to the extracted DNA information */
|
/** Obtain a reference to the extracted DNA information */
|
||||||
const Blender::DNA& GetDNA() const {
|
const Blender::DNA &GetDNA() const {
|
||||||
return db.dna;
|
return db.dna;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
FileDatabase &db;
|
||||||
FileDatabase& db;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -835,9 +793,8 @@ private:
|
||||||
*/
|
*/
|
||||||
bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
|
bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
|
||||||
|
|
||||||
|
} // namespace Blender
|
||||||
} // end Blend
|
} // namespace Assimp
|
||||||
} // end Assimp
|
|
||||||
|
|
||||||
#include "BlenderDNA.inl"
|
#include "BlenderDNA.inl"
|
||||||
|
|
|
@ -795,7 +795,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
|
||||||
const Structure* DNA :: Get (const std::string& ss) const
|
const Structure* DNA :: Get (const std::string& ss) const
|
||||||
{
|
{
|
||||||
std::map<std::string, size_t>::const_iterator it = indices.find(ss);
|
std::map<std::string, size_t>::const_iterator it = indices.find(ss);
|
||||||
return it == indices.end() ? NULL : &structures[(*it).second];
|
return it == indices.end() ? nullptr : &structures[(*it).second];
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
|
@ -68,7 +68,7 @@ static const fpCreateModifier creators[] = {
|
||||||
&god<BlenderModifier_Mirror>,
|
&god<BlenderModifier_Mirror>,
|
||||||
&god<BlenderModifier_Subdivision>,
|
&god<BlenderModifier_Subdivision>,
|
||||||
|
|
||||||
NULL // sentinel
|
nullptr // sentinel
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -127,7 +127,7 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_d
|
||||||
modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
|
modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
curgod = NULL;
|
curgod = nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const
|
||||||
|
|
||||||
// Only reverse the winding order if an odd number of axes were mirrored.
|
// Only reverse the winding order if an odd number of axes were mirrored.
|
||||||
if (xs * ys * zs < 0) {
|
if (xs * ys * zs < 0) {
|
||||||
for (unsigned int j = 0; j < mesh->mNumFaces; ++j ) {
|
for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
|
||||||
aiFace &face = mesh->mFaces[j];
|
aiFace &face = mesh->mFaces[j];
|
||||||
for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
|
for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
|
||||||
std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
|
std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
|
||||||
|
@ -267,18 +267,18 @@ void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data,
|
||||||
|
|
||||||
Subdivider::Algorithm algo;
|
Subdivider::Algorithm algo;
|
||||||
switch (mir.subdivType) {
|
switch (mir.subdivType) {
|
||||||
case SubsurfModifierData::TYPE_CatmullClarke:
|
case SubsurfModifierData::TYPE_CatmullClarke:
|
||||||
algo = Subdivider::CATMULL_CLARKE;
|
algo = Subdivider::CATMULL_CLARKE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SubsurfModifierData::TYPE_Simple:
|
case SubsurfModifierData::TYPE_Simple:
|
||||||
ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
|
ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
|
||||||
algo = Subdivider::CATMULL_CLARKE;
|
algo = Subdivider::CATMULL_CLARKE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
|
ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
|
std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
|
|
@ -0,0 +1,838 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (ASSIMP)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2020, ASSIMP Development 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 Development 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 BlenderScene.cpp
|
||||||
|
* @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||||
|
|
||||||
|
#include "BlenderScene.h"
|
||||||
|
#include "BlenderCustomData.h"
|
||||||
|
#include "BlenderDNA.h"
|
||||||
|
#include "BlenderSceneGen.h"
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
using namespace Assimp::Blender;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Object>(
|
||||||
|
Object &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
int temp = 0;
|
||||||
|
ReadField<ErrorPolicy_Fail>(temp, "type", db);
|
||||||
|
dest.type = static_cast<Assimp::Blender::Object::Type>(temp);
|
||||||
|
ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat, "obmat", db);
|
||||||
|
ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv, "parentinv", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr, "parsubstr", db);
|
||||||
|
{
|
||||||
|
std::shared_ptr<Object> parent;
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(parent, "*parent", db);
|
||||||
|
dest.parent = parent.get();
|
||||||
|
}
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.track, "*track", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy, "*proxy", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from, "*proxy_from", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group, "*proxy_group", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group, "*dup_group", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.data, "*data", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.modifiers, "modifiers", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Group>(
|
||||||
|
Group &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.layer, "layer", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject, "*gobject", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MTex>(
|
||||||
|
MTex &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
int temp_short = 0;
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp_short, "mapto", db);
|
||||||
|
dest.mapto = static_cast<Assimp::Blender::MTex::MapType>(temp_short);
|
||||||
|
int temp = 0;
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp, "blendtype", db);
|
||||||
|
dest.blendtype = static_cast<Assimp::Blender::MTex::BlendType>(temp);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.object, "*object", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.tex, "*tex", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Igno>(dest.uvname, "uvname", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp, "projx", db);
|
||||||
|
dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp, "projy", db);
|
||||||
|
dest.projy = static_cast<Assimp::Blender::MTex::Projection>(temp);
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp, "projz", db);
|
||||||
|
dest.projz = static_cast<Assimp::Blender::MTex::Projection>(temp);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mapping, "mapping", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Igno>(dest.ofs, "ofs", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Igno>(dest.size, "size", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.rot, "rot", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.texflag, "texflag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pmapto, "pmapto", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pmaptoneg, "pmaptoneg", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.colspecfac, "colspecfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mirrfac, "mirrfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.alphafac, "alphafac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.difffac, "difffac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.specfac, "specfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.emitfac, "emitfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.hardfac, "hardfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.norfac, "norfac", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<TFace>(
|
||||||
|
TFace &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Fail>(dest.col, "col", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<SubsurfModifierData>(
|
||||||
|
SubsurfModifierData &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.subdivType, "subdivType", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.levels, "levels", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.renderLevels, "renderLevels", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flags, "flags", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MFace>(
|
||||||
|
MFace &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v3, "v3", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v4, "v4", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.mat_nr, "mat_nr", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Lamp>(
|
||||||
|
Lamp &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
int temp = 0;
|
||||||
|
ReadField<ErrorPolicy_Fail>(temp, "type", db);
|
||||||
|
dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flags, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totex, "totex", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.energy, "energy", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.dist, "dist", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.spotsize, "spotsize", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.spotblend, "spotblend", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.att1, "att1", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.att2, "att2", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp, "falloff_type", db);
|
||||||
|
dest.falloff_type = static_cast<Assimp::Blender::Lamp::FalloffType>(temp);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sun_brightness, "sun_brightness", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.area_size, "area_size", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.area_sizey, "area_sizey", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.area_sizez, "area_sizez", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.area_shape, "area_shape", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MDeformWeight>(
|
||||||
|
MDeformWeight &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.def_nr, "def_nr", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.weight, "weight", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<PackedFile>(
|
||||||
|
PackedFile &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.size, "size", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.seek, "seek", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.data, "*data", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Base>(
|
||||||
|
Base &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
// note: as per https://github.com/assimp/assimp/issues/128,
|
||||||
|
// reading the Object linked list recursively is prone to stack overflow.
|
||||||
|
// This structure converter is therefore an hand-written exception that
|
||||||
|
// does it iteratively.
|
||||||
|
|
||||||
|
const int initial_pos = db.reader->GetCurrentPos();
|
||||||
|
|
||||||
|
std::pair<Base *, int> todo = std::make_pair(&dest, initial_pos);
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
Base &cur_dest = *todo.first;
|
||||||
|
db.reader->SetCurrentPos(todo.second);
|
||||||
|
|
||||||
|
// we know that this is a double-linked, circular list which we never
|
||||||
|
// traverse backwards, so don't bother resolving the back links.
|
||||||
|
cur_dest.prev = nullptr;
|
||||||
|
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.object, "*object", db);
|
||||||
|
|
||||||
|
// the return value of ReadFieldPtr indicates whether the object
|
||||||
|
// was already cached. In this case, we don't need to resolve
|
||||||
|
// it again.
|
||||||
|
if (!ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.next, "*next", db, true) && cur_dest.next) {
|
||||||
|
todo = std::make_pair(&*cur_dest.next, db.reader->GetCurrentPos());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.reader->SetCurrentPos(initial_pos + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MTFace>(
|
||||||
|
MTFace &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Material>(
|
||||||
|
Material &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.specr, "specr", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.specg, "specg", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.specb, "specb", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.har, "har", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.ambr, "ambr", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.ambg, "ambg", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.ambb, "ambb", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mirr, "mirr", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mirg, "mirg", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mirb, "mirb", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.emit, "emit", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ray_mirror, "ray_mirror", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.alpha, "alpha", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ref, "ref", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.translucency, "translucency", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.roughness, "roughness", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.darkness, "darkness", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.refrac, "refrac", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.group, "*group", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.diff_shader, "diff_shader", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.spec_shader, "spec_shader", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex, "*mtex", db);
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.amb, "amb", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ang, "ang", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.spectra, "spectra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.spec, "spec", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.zoffs, "zoffs", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.add, "add", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fresnel_mir, "fresnel_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fresnel_mir_i, "fresnel_mir_i", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fresnel_tra, "fresnel_tra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fresnel_tra_i, "fresnel_tra_i", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.filter, "filter", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tx_limit, "tx_limit", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tx_falloff, "tx_falloff", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.gloss_mir, "gloss_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.gloss_tra, "gloss_tra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_mir, "adapt_thresh_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_tra, "adapt_thresh_tra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.aniso_gloss_mir, "aniso_gloss_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.dist_mir, "dist_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.hasize, "hasize", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flaresize, "flaresize", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.subsize, "subsize", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flareboost, "flareboost", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_sta, "strand_sta", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_end, "strand_end", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_ease, "strand_ease", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_surfnor, "strand_surfnor", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_min, "strand_min", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.strand_widthfade, "strand_widthfade", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sbias, "sbias", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.lbias, "lbias", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.shad_alpha, "shad_alpha", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.param, "param", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.rms, "rms", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.rampfac_col, "rampfac_col", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.rampfac_spec, "rampfac_spec", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.friction, "friction", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fh, "fh", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.reflect, "reflect", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fhdist, "fhdist", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.xyfrict, "xyfrict", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_radius, "sss_radius", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_col, "sss_col", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_error, "sss_error", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_scale, "sss_scale", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_ior, "sss_ior", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_colfac, "sss_colfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_texfac, "sss_texfac", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_front, "sss_front", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_back, "sss_back", db);
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.material_type, "material_type", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ray_depth, "ray_depth", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ray_depth_tra, "ray_depth_tra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.samp_gloss_mir, "samp_gloss_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.samp_gloss_tra, "samp_gloss_tra", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fadeto_mir, "fadeto_mir", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.shade_flag, "shade_flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flarec, "flarec", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.starc, "starc", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.linec, "linec", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ringc, "ringc", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pr_lamp, "pr_lamp", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pr_texture, "pr_texture", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ml_flag, "ml_flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.diff_shader, "diff_shader", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.spec_shader, "spec_shader", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.texco, "texco", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mapto, "mapto", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ramp_show, "ramp_show", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad3, "pad3", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.dynamode, "dynamode", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad2, "pad2", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_flag, "sss_flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.sss_preset, "sss_preset", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.shadowonly_flag, "shadowonly_flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.index, "index", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.vcol_alpha, "vcol_alpha", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad4, "pad4", db);
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.seed1, "seed1", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.seed2, "seed2", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MTexPoly>(
|
||||||
|
MTexPoly &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
{
|
||||||
|
std::shared_ptr<Image> tpage;
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(tpage, "*tpage", db);
|
||||||
|
dest.tpage = tpage.get();
|
||||||
|
}
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.transp, "transp", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Mesh>(
|
||||||
|
Mesh &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.totface, "totface", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.totedge, "totedge", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.totvert, "totvert", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totpoly, "totpoly", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.subdiv, "subdiv", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.subdivr, "subdivr", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.subsurftype, "subsurftype", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.smoothresh, "smoothresh", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.mface, "*mface", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface, "*mtface", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.tface, "*tface", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert, "*mvert", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.medge, "*medge", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mloop, "*mloop", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopuv, "*mloopuv", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopcol, "*mloopcol", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mpoly, "*mpoly", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mtpoly, "*mtpoly", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert, "*dvert", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol, "*mcol", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.mat, "**mat", db);
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MDeformVert>(
|
||||||
|
MDeformVert &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.dw, "*dw", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totweight, "totweight", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<World>(
|
||||||
|
World &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MLoopCol>(
|
||||||
|
MLoopCol &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.r, "r", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.g, "g", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.b, "b", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.a, "a", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MVert>(
|
||||||
|
MVert &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Fail>(dest.no, "no", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
//ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MEdge>(
|
||||||
|
MEdge &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.crease, "crease", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MLoopUV>(
|
||||||
|
MLoopUV &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldArray<ErrorPolicy_Igno>(dest.uv, "uv", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<GroupObject>(
|
||||||
|
GroupObject &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.ob, "*ob", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<ListBase>(
|
||||||
|
ListBase &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.first, "*first", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.last, "*last", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MLoop>(
|
||||||
|
MLoop &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.v, "v", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.e, "e", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<ModifierData>(
|
||||||
|
ModifierData &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.next, "*next", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.prev, "*prev", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Igno>(dest.name, "name", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<ID>(
|
||||||
|
ID &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MCol>(
|
||||||
|
MCol &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.r, "r", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.g, "g", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.b, "b", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.a, "a", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MPoly>(
|
||||||
|
MPoly &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.loopstart, "loopstart", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.mat_nr, "mat_nr", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Scene>(
|
||||||
|
Scene &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.camera, "*camera", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.world, "*world", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.basact, "*basact", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.base, "base", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Library>(
|
||||||
|
Library &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Fail>(dest.filename, "filename", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.parent, "*parent", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Tex>(
|
||||||
|
Tex &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
short temp_short = 0;
|
||||||
|
ReadField<ErrorPolicy_Igno>(temp_short, "imaflag", db);
|
||||||
|
dest.imaflag = static_cast<Assimp::Blender::Tex::ImageFlags>(temp_short);
|
||||||
|
int temp = 0;
|
||||||
|
ReadField<ErrorPolicy_Fail>(temp, "type", db);
|
||||||
|
dest.type = static_cast<Assimp::Blender::Tex::Type>(temp);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Warn>(dest.ima, "*ima", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Camera>(
|
||||||
|
Camera &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
int temp = 0;
|
||||||
|
ReadField<ErrorPolicy_Warn>(temp, "type", db);
|
||||||
|
dest.type = static_cast<Assimp::Blender::Camera::Type>(temp);
|
||||||
|
ReadField<ErrorPolicy_Warn>(temp, "flag", db);
|
||||||
|
dest.flag = static_cast<Assimp::Blender::Camera::Type>(temp);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.lens, "lens", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.sensor_x, "sensor_x", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.clipsta, "clipsta", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.clipend, "clipend", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<MirrorModifierData>(
|
||||||
|
MirrorModifierData &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.axis, "axis", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tolerance, "tolerance", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob, "*mirror_ob", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure ::Convert<Image>(
|
||||||
|
Image &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.ok, "ok", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.source, "source", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.pad1, "pad1", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.lastframe, "lastframe", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.tpageflag, "tpageflag", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.totbind, "totbind", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.xrep, "xrep", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.yrep, "yrep", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.twsta, "twsta", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.twend, "twend", db);
|
||||||
|
ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile, "*packedfile", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.lastupdate, "lastupdate", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.lastused, "lastused", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.animspeed, "animspeed", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.gen_x, "gen_x", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.gen_y, "gen_y", db);
|
||||||
|
ReadField<ErrorPolicy_Igno>(dest.gen_type, "gen_type", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure::Convert<CustomData>(
|
||||||
|
CustomData &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
|
||||||
|
ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
|
||||||
|
ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
void Structure::Convert<CustomDataLayer>(
|
||||||
|
CustomDataLayer &dest,
|
||||||
|
const FileDatabase &db) const {
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
|
||||||
|
ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
|
||||||
|
ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
|
||||||
|
ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
|
||||||
|
|
||||||
|
db.reader->IncPtr(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
void DNA::RegisterConverters() {
|
||||||
|
|
||||||
|
converters["Object"] = DNA::FactoryPair(&Structure::Allocate<Object>, &Structure::Convert<Object>);
|
||||||
|
converters["Group"] = DNA::FactoryPair(&Structure::Allocate<Group>, &Structure::Convert<Group>);
|
||||||
|
converters["MTex"] = DNA::FactoryPair(&Structure::Allocate<MTex>, &Structure::Convert<MTex>);
|
||||||
|
converters["TFace"] = DNA::FactoryPair(&Structure::Allocate<TFace>, &Structure::Convert<TFace>);
|
||||||
|
converters["SubsurfModifierData"] = DNA::FactoryPair(&Structure::Allocate<SubsurfModifierData>, &Structure::Convert<SubsurfModifierData>);
|
||||||
|
converters["MFace"] = DNA::FactoryPair(&Structure::Allocate<MFace>, &Structure::Convert<MFace>);
|
||||||
|
converters["Lamp"] = DNA::FactoryPair(&Structure::Allocate<Lamp>, &Structure::Convert<Lamp>);
|
||||||
|
converters["MDeformWeight"] = DNA::FactoryPair(&Structure::Allocate<MDeformWeight>, &Structure::Convert<MDeformWeight>);
|
||||||
|
converters["PackedFile"] = DNA::FactoryPair(&Structure::Allocate<PackedFile>, &Structure::Convert<PackedFile>);
|
||||||
|
converters["Base"] = DNA::FactoryPair(&Structure::Allocate<Base>, &Structure::Convert<Base>);
|
||||||
|
converters["MTFace"] = DNA::FactoryPair(&Structure::Allocate<MTFace>, &Structure::Convert<MTFace>);
|
||||||
|
converters["Material"] = DNA::FactoryPair(&Structure::Allocate<Material>, &Structure::Convert<Material>);
|
||||||
|
converters["MTexPoly"] = DNA::FactoryPair(&Structure::Allocate<MTexPoly>, &Structure::Convert<MTexPoly>);
|
||||||
|
converters["Mesh"] = DNA::FactoryPair(&Structure::Allocate<Mesh>, &Structure::Convert<Mesh>);
|
||||||
|
converters["MDeformVert"] = DNA::FactoryPair(&Structure::Allocate<MDeformVert>, &Structure::Convert<MDeformVert>);
|
||||||
|
converters["World"] = DNA::FactoryPair(&Structure::Allocate<World>, &Structure::Convert<World>);
|
||||||
|
converters["MLoopCol"] = DNA::FactoryPair(&Structure::Allocate<MLoopCol>, &Structure::Convert<MLoopCol>);
|
||||||
|
converters["MVert"] = DNA::FactoryPair(&Structure::Allocate<MVert>, &Structure::Convert<MVert>);
|
||||||
|
converters["MEdge"] = DNA::FactoryPair(&Structure::Allocate<MEdge>, &Structure::Convert<MEdge>);
|
||||||
|
converters["MLoopUV"] = DNA::FactoryPair(&Structure::Allocate<MLoopUV>, &Structure::Convert<MLoopUV>);
|
||||||
|
converters["GroupObject"] = DNA::FactoryPair(&Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject>);
|
||||||
|
converters["ListBase"] = DNA::FactoryPair(&Structure::Allocate<ListBase>, &Structure::Convert<ListBase>);
|
||||||
|
converters["MLoop"] = DNA::FactoryPair(&Structure::Allocate<MLoop>, &Structure::Convert<MLoop>);
|
||||||
|
converters["ModifierData"] = DNA::FactoryPair(&Structure::Allocate<ModifierData>, &Structure::Convert<ModifierData>);
|
||||||
|
converters["ID"] = DNA::FactoryPair(&Structure::Allocate<ID>, &Structure::Convert<ID>);
|
||||||
|
converters["MCol"] = DNA::FactoryPair(&Structure::Allocate<MCol>, &Structure::Convert<MCol>);
|
||||||
|
converters["MPoly"] = DNA::FactoryPair(&Structure::Allocate<MPoly>, &Structure::Convert<MPoly>);
|
||||||
|
converters["Scene"] = DNA::FactoryPair(&Structure::Allocate<Scene>, &Structure::Convert<Scene>);
|
||||||
|
converters["Library"] = DNA::FactoryPair(&Structure::Allocate<Library>, &Structure::Convert<Library>);
|
||||||
|
converters["Tex"] = DNA::FactoryPair(&Structure::Allocate<Tex>, &Structure::Convert<Tex>);
|
||||||
|
converters["Camera"] = DNA::FactoryPair(&Structure::Allocate<Camera>, &Structure::Convert<Camera>);
|
||||||
|
converters["MirrorModifierData"] = DNA::FactoryPair(&Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData>);
|
||||||
|
converters["Image"] = DNA::FactoryPair(&Structure::Allocate<Image>, &Structure::Convert<Image>);
|
||||||
|
converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
|
||||||
|
converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER
|
|
@ -48,14 +48,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "BlenderDNA.h"
|
#include "BlenderDNA.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace Blender {
|
namespace Blender {
|
||||||
|
|
||||||
// Minor parts of this file are extracts from blender data structures,
|
// Minor parts of this file are extracts from blender data structures,
|
||||||
// declared in the ./source/blender/makesdna directory.
|
// declared in the ./source/blender/makesdna directory.
|
||||||
// Stuff that is not used by Assimp is commented.
|
// Stuff that is not used by Assimp is commented.
|
||||||
|
|
||||||
|
|
||||||
// NOTE
|
// NOTE
|
||||||
// this file serves as input data to the `./scripts/genblenddna.py`
|
// this file serves as input data to the `./scripts/genblenddna.py`
|
||||||
// script. This script generates the actual binding code to read a
|
// script. This script generates the actual binding code to read a
|
||||||
|
@ -95,15 +94,15 @@ namespace Blender {
|
||||||
|
|
||||||
// warn if field is missing, substitute default value
|
// warn if field is missing, substitute default value
|
||||||
#ifdef WARN
|
#ifdef WARN
|
||||||
# undef WARN
|
#undef WARN
|
||||||
#endif
|
#endif
|
||||||
#define WARN
|
#define WARN
|
||||||
|
|
||||||
// fail the import if the field does not exist
|
// fail the import if the field does not exist
|
||||||
#ifdef FAIL
|
#ifdef FAIL
|
||||||
# undef FAIL
|
#undef FAIL
|
||||||
#endif
|
#endif
|
||||||
#define FAIL
|
#define FAIL
|
||||||
|
|
||||||
struct Object;
|
struct Object;
|
||||||
struct MTex;
|
struct MTex;
|
||||||
|
@ -117,7 +116,7 @@ static const size_t MaxNameLen = 1024;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct ID : ElemBase {
|
struct ID : ElemBase {
|
||||||
char name[ MaxNameLen ] WARN;
|
char name[MaxNameLen] WARN;
|
||||||
short flag;
|
short flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,17 +126,16 @@ struct ListBase : ElemBase {
|
||||||
std::shared_ptr<ElemBase> last;
|
std::shared_ptr<ElemBase> last;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct PackedFile : ElemBase {
|
struct PackedFile : ElemBase {
|
||||||
int size WARN;
|
int size WARN;
|
||||||
int seek WARN;
|
int seek WARN;
|
||||||
std::shared_ptr< FileOffset > data WARN;
|
std::shared_ptr<FileOffset> data WARN;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct GroupObject : ElemBase {
|
struct GroupObject : ElemBase {
|
||||||
std::shared_ptr<GroupObject> prev,next FAIL;
|
std::shared_ptr<GroupObject> prev, next FAIL;
|
||||||
std::shared_ptr<Object> ob;
|
std::shared_ptr<Object> ob;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,23 +155,20 @@ struct World : ElemBase {
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MVert : ElemBase {
|
struct MVert : ElemBase {
|
||||||
float co[3] FAIL;
|
float co[3] FAIL;
|
||||||
float no[3] FAIL; // readed as short and divided through / 32767.f
|
float no[3] FAIL; // readed as short and divided through / 32767.f
|
||||||
char flag;
|
char flag;
|
||||||
int mat_nr WARN;
|
int mat_nr WARN;
|
||||||
int bweight;
|
int bweight;
|
||||||
|
|
||||||
MVert() : ElemBase()
|
MVert() :
|
||||||
, flag(0)
|
ElemBase(), flag(0), mat_nr(0), bweight(0) {}
|
||||||
, mat_nr(0)
|
|
||||||
, bweight(0)
|
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MEdge : ElemBase {
|
struct MEdge : ElemBase {
|
||||||
int v1, v2 FAIL;
|
int v1, v2 FAIL;
|
||||||
char crease, bweight;
|
char crease, bweight;
|
||||||
short flag;
|
short flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
|
@ -190,7 +185,7 @@ struct MLoopUV : ElemBase {
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
// Note that red and blue are not swapped, as with MCol
|
// Note that red and blue are not swapped, as with MCol
|
||||||
struct MLoopCol : ElemBase {
|
struct MLoopCol : ElemBase {
|
||||||
unsigned char r, g, b, a;
|
unsigned char r, g, b, a;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
|
@ -203,19 +198,19 @@ struct MPoly : ElemBase {
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MTexPoly : ElemBase {
|
struct MTexPoly : ElemBase {
|
||||||
Image* tpage;
|
Image *tpage;
|
||||||
char flag, transp;
|
char flag, transp;
|
||||||
short mode, tile, pad;
|
short mode, tile, pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MCol : ElemBase {
|
struct MCol : ElemBase {
|
||||||
char r,g,b,a FAIL;
|
char r, g, b, a FAIL;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MFace : ElemBase {
|
struct MFace : ElemBase {
|
||||||
int v1,v2,v3,v4 FAIL;
|
int v1, v2, v3, v4 FAIL;
|
||||||
int mat_nr FAIL;
|
int mat_nr FAIL;
|
||||||
char flag;
|
char flag;
|
||||||
};
|
};
|
||||||
|
@ -232,13 +227,9 @@ struct TFace : ElemBase {
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MTFace : ElemBase {
|
struct MTFace : ElemBase {
|
||||||
MTFace()
|
MTFace() :
|
||||||
: flag(0)
|
flag(0), mode(0), tile(0), unwrap(0) {
|
||||||
, mode(0)
|
}
|
||||||
, tile(0)
|
|
||||||
, unwrap(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
float uv[4][2] FAIL;
|
float uv[4][2] FAIL;
|
||||||
char flag;
|
char flag;
|
||||||
|
@ -250,31 +241,31 @@ struct MTFace : ElemBase {
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MDeformWeight : ElemBase {
|
struct MDeformWeight : ElemBase {
|
||||||
int def_nr FAIL;
|
int def_nr FAIL;
|
||||||
float weight FAIL;
|
float weight FAIL;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct MDeformVert : ElemBase {
|
struct MDeformVert : ElemBase {
|
||||||
vector<MDeformWeight> dw WARN;
|
vector<MDeformWeight> dw WARN;
|
||||||
int totweight;
|
int totweight;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
#define MA_RAYMIRROR 0x40000
|
#define MA_RAYMIRROR 0x40000
|
||||||
#define MA_TRANSPARENCY 0x10000
|
#define MA_TRANSPARENCY 0x10000
|
||||||
#define MA_RAYTRANSP 0x20000
|
#define MA_RAYTRANSP 0x20000
|
||||||
#define MA_ZTRANSP 0x00040
|
#define MA_ZTRANSP 0x00040
|
||||||
|
|
||||||
struct Material : ElemBase {
|
struct Material : ElemBase {
|
||||||
ID id FAIL;
|
ID id FAIL;
|
||||||
|
|
||||||
float r,g,b WARN;
|
float r, g, b WARN;
|
||||||
float specr,specg,specb WARN;
|
float specr, specg, specb WARN;
|
||||||
short har;
|
short har;
|
||||||
float ambr,ambg,ambb WARN;
|
float ambr, ambg, ambb WARN;
|
||||||
float mirr,mirg,mirb;
|
float mirr, mirg, mirb;
|
||||||
float emit WARN;
|
float emit WARN;
|
||||||
float ray_mirror;
|
float ray_mirror;
|
||||||
float alpha WARN;
|
float alpha WARN;
|
||||||
|
@ -399,20 +390,19 @@ struct CustomDataLayer : ElemBase {
|
||||||
int active_mask;
|
int active_mask;
|
||||||
int uid;
|
int uid;
|
||||||
char name[64];
|
char name[64];
|
||||||
std::shared_ptr<ElemBase> data; // must be converted to real type according type member
|
std::shared_ptr<ElemBase> data; // must be converted to real type according type member
|
||||||
|
|
||||||
CustomDataLayer()
|
CustomDataLayer() :
|
||||||
: ElemBase()
|
ElemBase(),
|
||||||
, type(0)
|
type(0),
|
||||||
, offset(0)
|
offset(0),
|
||||||
, flag(0)
|
flag(0),
|
||||||
, active(0)
|
active(0),
|
||||||
, active_rnd(0)
|
active_rnd(0),
|
||||||
, active_clone(0)
|
active_clone(0),
|
||||||
, active_mask(0)
|
active_mask(0),
|
||||||
, uid(0)
|
uid(0),
|
||||||
, data(nullptr)
|
data(nullptr) {
|
||||||
{
|
|
||||||
memset(name, 0, sizeof name);
|
memset(name, 0, sizeof name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -430,8 +420,8 @@ CustomData 208
|
||||||
CustomDataExternal *external 200 8
|
CustomDataExternal *external 200 8
|
||||||
*/
|
*/
|
||||||
struct CustomData : ElemBase {
|
struct CustomData : ElemBase {
|
||||||
vector<std::shared_ptr<struct CustomDataLayer> > layers;
|
vector<std::shared_ptr<struct CustomDataLayer>> layers;
|
||||||
int typemap[42]; // CD_NUMTYPES
|
int typemap[42]; // CD_NUMTYPES
|
||||||
int totlayer;
|
int totlayer;
|
||||||
int maxlayer;
|
int maxlayer;
|
||||||
int totsize;
|
int totsize;
|
||||||
|
@ -469,7 +459,7 @@ struct Mesh : ElemBase {
|
||||||
vector<MDeformVert> dvert;
|
vector<MDeformVert> dvert;
|
||||||
vector<MCol> mcol;
|
vector<MCol> mcol;
|
||||||
|
|
||||||
vector< std::shared_ptr<Material> > mat FAIL;
|
vector<std::shared_ptr<Material>> mat FAIL;
|
||||||
|
|
||||||
struct CustomData vdata;
|
struct CustomData vdata;
|
||||||
struct CustomData edata;
|
struct CustomData edata;
|
||||||
|
@ -490,142 +480,141 @@ struct Library : ElemBase {
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Camera : ElemBase {
|
struct Camera : ElemBase {
|
||||||
enum Type {
|
enum Type {
|
||||||
Type_PERSP = 0
|
Type_PERSP = 0,
|
||||||
,Type_ORTHO = 1
|
Type_ORTHO = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
ID id FAIL;
|
ID id FAIL;
|
||||||
|
|
||||||
Type type,flag WARN;
|
Type type, flag WARN;
|
||||||
float lens WARN;
|
float lens WARN;
|
||||||
float sensor_x WARN;
|
float sensor_x WARN;
|
||||||
float clipsta, clipend;
|
float clipsta, clipend;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Lamp : ElemBase {
|
struct Lamp : ElemBase {
|
||||||
|
|
||||||
enum FalloffType {
|
enum FalloffType {
|
||||||
FalloffType_Constant = 0x0
|
FalloffType_Constant = 0x0,
|
||||||
,FalloffType_InvLinear = 0x1
|
FalloffType_InvLinear = 0x1,
|
||||||
,FalloffType_InvSquare = 0x2
|
FalloffType_InvSquare = 0x2
|
||||||
//,FalloffType_Curve = 0x3
|
//,FalloffType_Curve = 0x3
|
||||||
//,FalloffType_Sliders = 0x4
|
//,FalloffType_Sliders = 0x4
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
Type_Local = 0x0
|
Type_Local = 0x0,
|
||||||
,Type_Sun = 0x1
|
Type_Sun = 0x1,
|
||||||
,Type_Spot = 0x2
|
Type_Spot = 0x2,
|
||||||
,Type_Hemi = 0x3
|
Type_Hemi = 0x3,
|
||||||
,Type_Area = 0x4
|
Type_Area = 0x4
|
||||||
//,Type_YFPhoton = 0x5
|
//,Type_YFPhoton = 0x5
|
||||||
};
|
};
|
||||||
|
|
||||||
ID id FAIL;
|
ID id FAIL;
|
||||||
//AnimData *adt;
|
//AnimData *adt;
|
||||||
|
|
||||||
Type type FAIL;
|
Type type FAIL;
|
||||||
short flags;
|
short flags;
|
||||||
|
|
||||||
//int mode;
|
//int mode;
|
||||||
|
|
||||||
short colormodel, totex;
|
short colormodel, totex;
|
||||||
float r,g,b,k WARN;
|
float r, g, b, k WARN;
|
||||||
//float shdwr, shdwg, shdwb;
|
//float shdwr, shdwg, shdwb;
|
||||||
|
|
||||||
float energy, dist, spotsize, spotblend;
|
float energy, dist, spotsize, spotblend;
|
||||||
//float haint;
|
//float haint;
|
||||||
|
|
||||||
float constant_coefficient;
|
float constant_coefficient;
|
||||||
float linear_coefficient;
|
float linear_coefficient;
|
||||||
float quadratic_coefficient;
|
float quadratic_coefficient;
|
||||||
|
|
||||||
float att1, att2;
|
float att1, att2;
|
||||||
//struct CurveMapping *curfalloff;
|
//struct CurveMapping *curfalloff;
|
||||||
FalloffType falloff_type;
|
FalloffType falloff_type;
|
||||||
|
|
||||||
//float clipsta, clipend, shadspotsize;
|
//float clipsta, clipend, shadspotsize;
|
||||||
//float bias, soft, compressthresh;
|
//float bias, soft, compressthresh;
|
||||||
//short bufsize, samp, buffers, filtertype;
|
//short bufsize, samp, buffers, filtertype;
|
||||||
//char bufflag, buftype;
|
//char bufflag, buftype;
|
||||||
|
|
||||||
//short ray_samp, ray_sampy, ray_sampz;
|
//short ray_samp, ray_sampy, ray_sampz;
|
||||||
//short ray_samp_type;
|
//short ray_samp_type;
|
||||||
short area_shape;
|
short area_shape;
|
||||||
float area_size, area_sizey, area_sizez;
|
float area_size, area_sizey, area_sizez;
|
||||||
//float adapt_thresh;
|
//float adapt_thresh;
|
||||||
//short ray_samp_method;
|
//short ray_samp_method;
|
||||||
|
|
||||||
//short texact, shadhalostep;
|
//short texact, shadhalostep;
|
||||||
|
|
||||||
//short sun_effect_type;
|
//short sun_effect_type;
|
||||||
//short skyblendtype;
|
//short skyblendtype;
|
||||||
//float horizon_brightness;
|
//float horizon_brightness;
|
||||||
//float spread;
|
//float spread;
|
||||||
float sun_brightness;
|
float sun_brightness;
|
||||||
//float sun_size;
|
//float sun_size;
|
||||||
//float backscattered_light;
|
//float backscattered_light;
|
||||||
//float sun_intensity;
|
//float sun_intensity;
|
||||||
//float atm_turbidity;
|
//float atm_turbidity;
|
||||||
//float atm_inscattering_factor;
|
//float atm_inscattering_factor;
|
||||||
//float atm_extinction_factor;
|
//float atm_extinction_factor;
|
||||||
//float atm_distance_factor;
|
//float atm_distance_factor;
|
||||||
//float skyblendfac;
|
//float skyblendfac;
|
||||||
//float sky_exposure;
|
//float sky_exposure;
|
||||||
//short sky_colorspace;
|
//short sky_colorspace;
|
||||||
|
|
||||||
// int YF_numphotons, YF_numsearch;
|
// int YF_numphotons, YF_numsearch;
|
||||||
// short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
|
// short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
|
||||||
// float YF_causticblur, YF_ltradius;
|
// float YF_causticblur, YF_ltradius;
|
||||||
|
|
||||||
// float YF_glowint, YF_glowofs;
|
// float YF_glowint, YF_glowofs;
|
||||||
// short YF_glowtype, YF_pad2;
|
// short YF_glowtype, YF_pad2;
|
||||||
|
|
||||||
//struct Ipo *ipo;
|
//struct Ipo *ipo;
|
||||||
//struct MTex *mtex[18];
|
//struct MTex *mtex[18];
|
||||||
// short pr_texture;
|
// short pr_texture;
|
||||||
|
|
||||||
//struct PreviewImage *preview;
|
//struct PreviewImage *preview;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct ModifierData : ElemBase {
|
struct ModifierData : ElemBase {
|
||||||
enum ModifierType {
|
enum ModifierType {
|
||||||
eModifierType_None = 0,
|
eModifierType_None = 0,
|
||||||
eModifierType_Subsurf,
|
eModifierType_Subsurf,
|
||||||
eModifierType_Lattice,
|
eModifierType_Lattice,
|
||||||
eModifierType_Curve,
|
eModifierType_Curve,
|
||||||
eModifierType_Build,
|
eModifierType_Build,
|
||||||
eModifierType_Mirror,
|
eModifierType_Mirror,
|
||||||
eModifierType_Decimate,
|
eModifierType_Decimate,
|
||||||
eModifierType_Wave,
|
eModifierType_Wave,
|
||||||
eModifierType_Armature,
|
eModifierType_Armature,
|
||||||
eModifierType_Hook,
|
eModifierType_Hook,
|
||||||
eModifierType_Softbody,
|
eModifierType_Softbody,
|
||||||
eModifierType_Boolean,
|
eModifierType_Boolean,
|
||||||
eModifierType_Array,
|
eModifierType_Array,
|
||||||
eModifierType_EdgeSplit,
|
eModifierType_EdgeSplit,
|
||||||
eModifierType_Displace,
|
eModifierType_Displace,
|
||||||
eModifierType_UVProject,
|
eModifierType_UVProject,
|
||||||
eModifierType_Smooth,
|
eModifierType_Smooth,
|
||||||
eModifierType_Cast,
|
eModifierType_Cast,
|
||||||
eModifierType_MeshDeform,
|
eModifierType_MeshDeform,
|
||||||
eModifierType_ParticleSystem,
|
eModifierType_ParticleSystem,
|
||||||
eModifierType_ParticleInstance,
|
eModifierType_ParticleInstance,
|
||||||
eModifierType_Explode,
|
eModifierType_Explode,
|
||||||
eModifierType_Cloth,
|
eModifierType_Cloth,
|
||||||
eModifierType_Collision,
|
eModifierType_Collision,
|
||||||
eModifierType_Bevel,
|
eModifierType_Bevel,
|
||||||
eModifierType_Shrinkwrap,
|
eModifierType_Shrinkwrap,
|
||||||
eModifierType_Fluidsim,
|
eModifierType_Fluidsim,
|
||||||
eModifierType_Mask,
|
eModifierType_Mask,
|
||||||
eModifierType_SimpleDeform,
|
eModifierType_SimpleDeform,
|
||||||
eModifierType_Multires,
|
eModifierType_Multires,
|
||||||
eModifierType_Surface,
|
eModifierType_Surface,
|
||||||
eModifierType_Smoke,
|
eModifierType_Smoke,
|
||||||
eModifierType_ShapeKey
|
eModifierType_ShapeKey
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<ElemBase> next WARN;
|
std::shared_ptr<ElemBase> next WARN;
|
||||||
|
@ -636,7 +625,7 @@ struct ModifierData : ElemBase {
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct SubsurfModifierData : ElemBase {
|
struct SubsurfModifierData : ElemBase {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
|
|
||||||
|
@ -646,13 +635,13 @@ struct SubsurfModifierData : ElemBase {
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
// some omitted
|
// some omitted
|
||||||
FLAGS_SubsurfUV =1<<3
|
FLAGS_SubsurfUV = 1 << 3
|
||||||
};
|
};
|
||||||
|
|
||||||
ModifierData modifier FAIL;
|
ModifierData modifier FAIL;
|
||||||
short subdivType WARN;
|
short subdivType WARN;
|
||||||
short levels FAIL;
|
short levels FAIL;
|
||||||
short renderLevels ;
|
short renderLevels;
|
||||||
short flags;
|
short flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -660,13 +649,13 @@ struct SubsurfModifierData : ElemBase {
|
||||||
struct MirrorModifierData : ElemBase {
|
struct MirrorModifierData : ElemBase {
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
Flags_CLIPPING =1<<0,
|
Flags_CLIPPING = 1 << 0,
|
||||||
Flags_MIRROR_U =1<<1,
|
Flags_MIRROR_U = 1 << 1,
|
||||||
Flags_MIRROR_V =1<<2,
|
Flags_MIRROR_V = 1 << 2,
|
||||||
Flags_AXIS_X =1<<3,
|
Flags_AXIS_X = 1 << 3,
|
||||||
Flags_AXIS_Y =1<<4,
|
Flags_AXIS_Y = 1 << 4,
|
||||||
Flags_AXIS_Z =1<<5,
|
Flags_AXIS_Z = 1 << 5,
|
||||||
Flags_VGROUP =1<<6
|
Flags_VGROUP = 1 << 6
|
||||||
};
|
};
|
||||||
|
|
||||||
ModifierData modifier FAIL;
|
ModifierData modifier FAIL;
|
||||||
|
@ -677,22 +666,24 @@ struct MirrorModifierData : ElemBase {
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Object : ElemBase {
|
struct Object : ElemBase {
|
||||||
ID id FAIL;
|
ID id FAIL;
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
Type_EMPTY = 0
|
Type_EMPTY = 0,
|
||||||
,Type_MESH = 1
|
Type_MESH = 1,
|
||||||
,Type_CURVE = 2
|
Type_CURVE = 2,
|
||||||
,Type_SURF = 3
|
Type_SURF = 3,
|
||||||
,Type_FONT = 4
|
Type_FONT = 4,
|
||||||
,Type_MBALL = 5
|
Type_MBALL = 5
|
||||||
|
|
||||||
,Type_LAMP = 10
|
,
|
||||||
,Type_CAMERA = 11
|
Type_LAMP = 10,
|
||||||
|
Type_CAMERA = 11
|
||||||
|
|
||||||
,Type_WAVE = 21
|
,
|
||||||
,Type_LATTICE = 22
|
Type_WAVE = 21,
|
||||||
|
Type_LATTICE = 22
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type FAIL;
|
Type type FAIL;
|
||||||
|
@ -700,39 +691,29 @@ struct Object : ElemBase {
|
||||||
float parentinv[4][4] WARN;
|
float parentinv[4][4] WARN;
|
||||||
char parsubstr[32] WARN;
|
char parsubstr[32] WARN;
|
||||||
|
|
||||||
Object* parent WARN;
|
Object *parent WARN;
|
||||||
std::shared_ptr<Object> track WARN;
|
std::shared_ptr<Object> track WARN;
|
||||||
|
|
||||||
std::shared_ptr<Object> proxy,proxy_from,proxy_group WARN;
|
std::shared_ptr<Object> proxy, proxy_from, proxy_group WARN;
|
||||||
std::shared_ptr<Group> dup_group WARN;
|
std::shared_ptr<Group> dup_group WARN;
|
||||||
std::shared_ptr<ElemBase> data FAIL;
|
std::shared_ptr<ElemBase> data FAIL;
|
||||||
|
|
||||||
ListBase modifiers;
|
ListBase modifiers;
|
||||||
|
|
||||||
Object()
|
Object() :
|
||||||
: ElemBase()
|
ElemBase(), type(Type_EMPTY), parent(nullptr), track(), proxy(), proxy_from(), data() {
|
||||||
, type( Type_EMPTY )
|
|
||||||
, parent( nullptr )
|
|
||||||
, track()
|
|
||||||
, proxy()
|
|
||||||
, proxy_from()
|
|
||||||
, data() {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
struct Base : ElemBase {
|
struct Base : ElemBase {
|
||||||
Base* prev WARN;
|
Base *prev WARN;
|
||||||
std::shared_ptr<Base> next WARN;
|
std::shared_ptr<Base> next WARN;
|
||||||
std::shared_ptr<Object> object WARN;
|
std::shared_ptr<Object> object WARN;
|
||||||
|
|
||||||
Base()
|
Base() :
|
||||||
: ElemBase()
|
ElemBase(), prev(nullptr), next(), object() {
|
||||||
, prev( nullptr )
|
|
||||||
, next()
|
|
||||||
, object() {
|
|
||||||
// empty
|
// empty
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
@ -748,11 +729,8 @@ struct Scene : ElemBase {
|
||||||
|
|
||||||
ListBase base;
|
ListBase base;
|
||||||
|
|
||||||
Scene()
|
Scene() :
|
||||||
: ElemBase()
|
ElemBase(), camera(), world(), basact() {
|
||||||
, camera()
|
|
||||||
, world()
|
|
||||||
, basact() {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -783,9 +761,9 @@ struct Image : ElemBase {
|
||||||
short animspeed;
|
short animspeed;
|
||||||
|
|
||||||
short gen_x, gen_y, gen_type;
|
short gen_x, gen_y, gen_type;
|
||||||
|
|
||||||
Image()
|
Image() :
|
||||||
: ElemBase() {
|
ElemBase() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -795,33 +773,33 @@ struct Tex : ElemBase {
|
||||||
|
|
||||||
// actually, the only texture type we support is Type_IMAGE
|
// actually, the only texture type we support is Type_IMAGE
|
||||||
enum Type {
|
enum Type {
|
||||||
Type_CLOUDS = 1
|
Type_CLOUDS = 1,
|
||||||
,Type_WOOD = 2
|
Type_WOOD = 2,
|
||||||
,Type_MARBLE = 3
|
Type_MARBLE = 3,
|
||||||
,Type_MAGIC = 4
|
Type_MAGIC = 4,
|
||||||
,Type_BLEND = 5
|
Type_BLEND = 5,
|
||||||
,Type_STUCCI = 6
|
Type_STUCCI = 6,
|
||||||
,Type_NOISE = 7
|
Type_NOISE = 7,
|
||||||
,Type_IMAGE = 8
|
Type_IMAGE = 8,
|
||||||
,Type_PLUGIN = 9
|
Type_PLUGIN = 9,
|
||||||
,Type_ENVMAP = 10
|
Type_ENVMAP = 10,
|
||||||
,Type_MUSGRAVE = 11
|
Type_MUSGRAVE = 11,
|
||||||
,Type_VORONOI = 12
|
Type_VORONOI = 12,
|
||||||
,Type_DISTNOISE = 13
|
Type_DISTNOISE = 13,
|
||||||
,Type_POINTDENSITY = 14
|
Type_POINTDENSITY = 14,
|
||||||
,Type_VOXELDATA = 15
|
Type_VOXELDATA = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ImageFlags {
|
enum ImageFlags {
|
||||||
ImageFlags_INTERPOL = 1
|
ImageFlags_INTERPOL = 1,
|
||||||
,ImageFlags_USEALPHA = 2
|
ImageFlags_USEALPHA = 2,
|
||||||
,ImageFlags_MIPMAP = 4
|
ImageFlags_MIPMAP = 4,
|
||||||
,ImageFlags_IMAROT = 16
|
ImageFlags_IMAROT = 16,
|
||||||
,ImageFlags_CALCALPHA = 32
|
ImageFlags_CALCALPHA = 32,
|
||||||
,ImageFlags_NORMALMAP = 2048
|
ImageFlags_NORMALMAP = 2048,
|
||||||
,ImageFlags_GAUSS_MIP = 4096
|
ImageFlags_GAUSS_MIP = 4096,
|
||||||
,ImageFlags_FILTER_MIN = 8192
|
ImageFlags_FILTER_MIN = 8192,
|
||||||
,ImageFlags_DERIVATIVEMAP = 16384
|
ImageFlags_DERIVATIVEMAP = 16384
|
||||||
};
|
};
|
||||||
|
|
||||||
ID id FAIL;
|
ID id FAIL;
|
||||||
|
@ -876,11 +854,8 @@ struct Tex : ElemBase {
|
||||||
|
|
||||||
//char use_nodes;
|
//char use_nodes;
|
||||||
|
|
||||||
Tex()
|
Tex() :
|
||||||
: ElemBase()
|
ElemBase(), imaflag(ImageFlags_INTERPOL), type(Type_CLOUDS), ima() {
|
||||||
, imaflag( ImageFlags_INTERPOL )
|
|
||||||
, type( Type_CLOUDS )
|
|
||||||
, ima() {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -889,52 +864,52 @@ struct Tex : ElemBase {
|
||||||
struct MTex : ElemBase {
|
struct MTex : ElemBase {
|
||||||
|
|
||||||
enum Projection {
|
enum Projection {
|
||||||
Proj_N = 0
|
Proj_N = 0,
|
||||||
,Proj_X = 1
|
Proj_X = 1,
|
||||||
,Proj_Y = 2
|
Proj_Y = 2,
|
||||||
,Proj_Z = 3
|
Proj_Z = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Flag {
|
enum Flag {
|
||||||
Flag_RGBTOINT = 0x1
|
Flag_RGBTOINT = 0x1,
|
||||||
,Flag_STENCIL = 0x2
|
Flag_STENCIL = 0x2,
|
||||||
,Flag_NEGATIVE = 0x4
|
Flag_NEGATIVE = 0x4,
|
||||||
,Flag_ALPHAMIX = 0x8
|
Flag_ALPHAMIX = 0x8,
|
||||||
,Flag_VIEWSPACE = 0x10
|
Flag_VIEWSPACE = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BlendType {
|
enum BlendType {
|
||||||
BlendType_BLEND = 0
|
BlendType_BLEND = 0,
|
||||||
,BlendType_MUL = 1
|
BlendType_MUL = 1,
|
||||||
,BlendType_ADD = 2
|
BlendType_ADD = 2,
|
||||||
,BlendType_SUB = 3
|
BlendType_SUB = 3,
|
||||||
,BlendType_DIV = 4
|
BlendType_DIV = 4,
|
||||||
,BlendType_DARK = 5
|
BlendType_DARK = 5,
|
||||||
,BlendType_DIFF = 6
|
BlendType_DIFF = 6,
|
||||||
,BlendType_LIGHT = 7
|
BlendType_LIGHT = 7,
|
||||||
,BlendType_SCREEN = 8
|
BlendType_SCREEN = 8,
|
||||||
,BlendType_OVERLAY = 9
|
BlendType_OVERLAY = 9,
|
||||||
,BlendType_BLEND_HUE = 10
|
BlendType_BLEND_HUE = 10,
|
||||||
,BlendType_BLEND_SAT = 11
|
BlendType_BLEND_SAT = 11,
|
||||||
,BlendType_BLEND_VAL = 12
|
BlendType_BLEND_VAL = 12,
|
||||||
,BlendType_BLEND_COLOR = 13
|
BlendType_BLEND_COLOR = 13
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MapType {
|
enum MapType {
|
||||||
MapType_COL = 1
|
MapType_COL = 1,
|
||||||
,MapType_NORM = 2
|
MapType_NORM = 2,
|
||||||
,MapType_COLSPEC = 4
|
MapType_COLSPEC = 4,
|
||||||
,MapType_COLMIR = 8
|
MapType_COLMIR = 8,
|
||||||
,MapType_REF = 16
|
MapType_REF = 16,
|
||||||
,MapType_SPEC = 32
|
MapType_SPEC = 32,
|
||||||
,MapType_EMIT = 64
|
MapType_EMIT = 64,
|
||||||
,MapType_ALPHA = 128
|
MapType_ALPHA = 128,
|
||||||
,MapType_HAR = 256
|
MapType_HAR = 256,
|
||||||
,MapType_RAYMIRR = 512
|
MapType_RAYMIRR = 512,
|
||||||
,MapType_TRANSLU = 1024
|
MapType_TRANSLU = 1024,
|
||||||
,MapType_AMB = 2048
|
MapType_AMB = 2048,
|
||||||
,MapType_DISPLACE = 4096
|
MapType_DISPLACE = 4096,
|
||||||
,MapType_WARP = 8192
|
MapType_WARP = 8192
|
||||||
};
|
};
|
||||||
|
|
||||||
// short texco, maptoneg;
|
// short texco, maptoneg;
|
||||||
|
@ -945,7 +920,7 @@ struct MTex : ElemBase {
|
||||||
std::shared_ptr<Tex> tex;
|
std::shared_ptr<Tex> tex;
|
||||||
char uvname[32];
|
char uvname[32];
|
||||||
|
|
||||||
Projection projx,projy,projz;
|
Projection projx, projy, projz;
|
||||||
char mapping;
|
char mapping;
|
||||||
float ofs[3], size[3], rot;
|
float ofs[3], size[3], rot;
|
||||||
|
|
||||||
|
@ -953,7 +928,7 @@ struct MTex : ElemBase {
|
||||||
short colormodel, pmapto, pmaptoneg;
|
short colormodel, pmapto, pmaptoneg;
|
||||||
//short normapspace, which_output;
|
//short normapspace, which_output;
|
||||||
//char brush_map_mode;
|
//char brush_map_mode;
|
||||||
float r,g,b,k WARN;
|
float r, g, b, k WARN;
|
||||||
//float def_var, rt;
|
//float def_var, rt;
|
||||||
|
|
||||||
//float colfac, varfac;
|
//float colfac, varfac;
|
||||||
|
@ -972,12 +947,12 @@ struct MTex : ElemBase {
|
||||||
//float shadowfac;
|
//float shadowfac;
|
||||||
//float zenupfac, zendownfac, blendfac;
|
//float zenupfac, zendownfac, blendfac;
|
||||||
|
|
||||||
MTex()
|
MTex() :
|
||||||
: ElemBase() {
|
ElemBase() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace Blender
|
||||||
}
|
} // namespace Assimp
|
||||||
#endif
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -48,8 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
|
||||||
|
|
||||||
#include "DXF/DXFLoader.h"
|
#include "AssetLib/DXF/DXFLoader.h"
|
||||||
#include "DXF/DXFHelper.h"
|
#include "AssetLib/DXF/DXFHelper.h"
|
||||||
#include "PostProcessing/ConvertToLHProcess.h"
|
#include "PostProcessing/ConvertToLHProcess.h"
|
||||||
|
|
||||||
#include <assimp/ParsingUtils.h>
|
#include <assimp/ParsingUtils.h>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue