Move format importer and exporter into its won folder.
parent
52122a9900
commit
f8e6512a63
|
@ -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
|
||||
#include "3DSLoader.h"
|
||||
#include "Common/TargetAnimation.h"
|
||||
#include <assimp/StringComparison.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/DefaultLogger.hpp>
|
||||
#include <assimp/StringComparison.h>
|
||||
#include <memory>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
|
||||
using namespace Assimp;
|
||||
|
||||
|
@ -58,42 +58,39 @@ static const unsigned int NotSet = 0xcdcdcdcd;
|
|||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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
|
||||
// typical default material setting:
|
||||
// - no textures
|
||||
// - diffuse color (in grey!)
|
||||
// NOTE: This is here to workaround the fact that some
|
||||
// exporters are writing a default material, too.
|
||||
unsigned int idx( NotSet );
|
||||
for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
|
||||
{
|
||||
unsigned int idx(NotSet);
|
||||
for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
|
||||
std::string s = mScene->mMaterials[i].mName;
|
||||
for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
|
||||
*it = static_cast< char >( ::tolower( *it ) );
|
||||
for (std::string::iterator it = s.begin(); it != s.end(); ++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 !=
|
||||
mScene->mMaterials[i].mDiffuse.g ||
|
||||
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 ||
|
||||
mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
|
||||
mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
|
||||
mScene->mMaterials[i].sTexEmissive.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;
|
||||
}
|
||||
idx = i;
|
||||
}
|
||||
if ( NotSet == idx ) {
|
||||
idx = ( unsigned int )mScene->mMaterials.size();
|
||||
if (NotSet == idx) {
|
||||
idx = (unsigned int)mScene->mMaterials.size();
|
||||
}
|
||||
|
||||
// now iterate through all meshes and through all faces and
|
||||
|
@ -101,32 +98,26 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
|
|||
unsigned int cnt = 0;
|
||||
for (std::vector<D3DS::Mesh>::iterator
|
||||
i = mScene->mMeshes.begin();
|
||||
i != mScene->mMeshes.end();++i)
|
||||
{
|
||||
i != mScene->mMeshes.end(); ++i) {
|
||||
for (std::vector<unsigned int>::iterator
|
||||
a = (*i).mFaceMaterials.begin();
|
||||
a != (*i).mFaceMaterials.end();++a)
|
||||
{
|
||||
a != (*i).mFaceMaterials.end(); ++a) {
|
||||
// NOTE: The additional check seems to be necessary,
|
||||
// some exporters seem to generate invalid data here
|
||||
if (0xcdcdcdcd == (*a))
|
||||
{
|
||||
if (0xcdcdcdcd == (*a)) {
|
||||
(*a) = idx;
|
||||
++cnt;
|
||||
}
|
||||
else if ( (*a) >= mScene->mMaterials.size())
|
||||
{
|
||||
} else if ((*a) >= mScene->mMaterials.size()) {
|
||||
(*a) = idx;
|
||||
ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cnt && idx == mScene->mMaterials.size())
|
||||
{
|
||||
if (cnt && idx == mScene->mMaterials.size()) {
|
||||
// We need to create our own default material
|
||||
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);
|
||||
|
||||
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
|
||||
void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
|
||||
{
|
||||
for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
|
||||
{
|
||||
void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
|
||||
for (std::vector<D3DS::Face>::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
|
||||
// check whether all indices are in range
|
||||
for (unsigned int a = 0; a < 3;++a)
|
||||
{
|
||||
if ((*i).mIndices[a] >= sMesh.mPositions.size())
|
||||
{
|
||||
for (unsigned int a = 0; a < 3; ++a) {
|
||||
if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
|
||||
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)");
|
||||
(*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
|
||||
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
|
||||
// to do it now in a separate buffer.
|
||||
|
||||
// Allocate output storage
|
||||
std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
|
||||
std::vector<aiVector3D> vNew(sMesh.mFaces.size() * 3);
|
||||
std::vector<aiVector3D> vNew2;
|
||||
if (sMesh.mTexCoords.size())
|
||||
vNew2.resize(sMesh.mFaces.size() * 3);
|
||||
|
||||
for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
|
||||
{
|
||||
D3DS::Face& face = sMesh.mFaces[i];
|
||||
for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
|
||||
D3DS::Face &face = sMesh.mFaces[i];
|
||||
|
||||
// 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]];
|
||||
if (sMesh.mTexCoords.size())
|
||||
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
|
||||
void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
|
||||
{
|
||||
void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
|
||||
// Setup the texture name
|
||||
aiString tex;
|
||||
tex.Set( texture.mMapName);
|
||||
mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
|
||||
tex.Set(texture.mMapName);
|
||||
mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
|
||||
|
||||
// Setup the texture blend factor
|
||||
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
|
||||
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_V(type,0));
|
||||
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0));
|
||||
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0));
|
||||
|
||||
// Mirroring - double the scaling values
|
||||
// FIXME: this is not really correct ...
|
||||
if (texture.mMapMode == aiTextureMapMode_Mirror)
|
||||
{
|
||||
if (texture.mMapMode == aiTextureMapMode_Mirror) {
|
||||
texture.mScaleU *= 2.0;
|
||||
texture.mScaleV *= 2.0;
|
||||
texture.mOffsetU /= 2.0;
|
||||
|
@ -216,21 +197,19 @@ void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
|
|||
}
|
||||
|
||||
// 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
|
||||
void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
|
||||
aiMaterial& mat)
|
||||
{
|
||||
void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
|
||||
aiMaterial &mat) {
|
||||
// NOTE: Pass the background image to the viewer by bypassing the
|
||||
// material system. This is an evil hack, never do it again!
|
||||
if (0 != mBackgroundImage.length() && bHasBG)
|
||||
{
|
||||
if (0 != mBackgroundImage.length() && bHasBG) {
|
||||
aiString tex;
|
||||
tex.Set( mBackgroundImage);
|
||||
mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
|
||||
tex.Set(mBackgroundImage);
|
||||
mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
|
||||
|
||||
// Be sure this is only done for the first material
|
||||
mBackgroundImage = std::string("");
|
||||
|
@ -242,143 +221,138 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
|
|||
oldMat.mAmbient.b += mClrAmbient.b;
|
||||
|
||||
aiString name;
|
||||
name.Set( oldMat.mName);
|
||||
mat.AddProperty( &name, AI_MATKEY_NAME);
|
||||
name.Set(oldMat.mName);
|
||||
mat.AddProperty(&name, AI_MATKEY_NAME);
|
||||
|
||||
// Material colors
|
||||
mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
||||
mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||
mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
||||
mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
||||
mat.AddProperty(&oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
||||
mat.AddProperty(&oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||
mat.AddProperty(&oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
||||
mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
||||
|
||||
// Phong shininess and shininess strength
|
||||
if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
|
||||
D3DS::Discreet3DS::Metal == oldMat.mShading)
|
||||
{
|
||||
if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
|
||||
{
|
||||
D3DS::Discreet3DS::Metal == oldMat.mShading) {
|
||||
if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
|
||||
oldMat.mShading = D3DS::Discreet3DS::Gouraud;
|
||||
}
|
||||
else
|
||||
{
|
||||
mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
|
||||
mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
|
||||
} else {
|
||||
mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
|
||||
mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Opacity
|
||||
mat.AddProperty<ai_real>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
|
||||
mat.AddProperty<ai_real>(&oldMat.mTransparency, 1, AI_MATKEY_OPACITY);
|
||||
|
||||
// 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?
|
||||
if (oldMat.mTwoSided)
|
||||
{
|
||||
if (oldMat.mTwoSided) {
|
||||
int i = 1;
|
||||
mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
|
||||
mat.AddProperty<int>(&i, 1, AI_MATKEY_TWOSIDED);
|
||||
}
|
||||
|
||||
// Shading mode
|
||||
aiShadingMode eShading = aiShadingMode_NoShading;
|
||||
switch (oldMat.mShading)
|
||||
{
|
||||
switch (oldMat.mShading) {
|
||||
case D3DS::Discreet3DS::Flat:
|
||||
eShading = aiShadingMode_Flat; break;
|
||||
eShading = aiShadingMode_Flat;
|
||||
break;
|
||||
|
||||
// I don't know what "Wire" shading should be,
|
||||
// assume it is simple lambertian diffuse shading
|
||||
case D3DS::Discreet3DS::Wire:
|
||||
{
|
||||
case D3DS::Discreet3DS::Wire: {
|
||||
// Set the wireframe flag
|
||||
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:
|
||||
eShading = aiShadingMode_Gouraud; break;
|
||||
eShading = aiShadingMode_Gouraud;
|
||||
break;
|
||||
|
||||
// assume cook-torrance shading for metals.
|
||||
case D3DS::Discreet3DS::Phong :
|
||||
eShading = aiShadingMode_Phong; break;
|
||||
case D3DS::Discreet3DS::Phong:
|
||||
eShading = aiShadingMode_Phong;
|
||||
break;
|
||||
|
||||
case D3DS::Discreet3DS::Metal :
|
||||
eShading = aiShadingMode_CookTorrance; break;
|
||||
case D3DS::Discreet3DS::Metal:
|
||||
eShading = aiShadingMode_CookTorrance;
|
||||
break;
|
||||
|
||||
// FIX to workaround a warning with GCC 4 who complained
|
||||
// about a missing case Blinn: here - Blinn isn't a valid
|
||||
// value in the 3DS Loader, it is just needed for ASE
|
||||
case D3DS::Discreet3DS::Blinn :
|
||||
eShading = aiShadingMode_Blinn; break;
|
||||
case D3DS::Discreet3DS::Blinn:
|
||||
eShading = aiShadingMode_Blinn;
|
||||
break;
|
||||
}
|
||||
int eShading_ = static_cast<int>(eShading);
|
||||
mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
|
||||
|
||||
// DIFFUSE texture
|
||||
if( oldMat.sTexDiffuse.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
|
||||
if (oldMat.sTexDiffuse.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
|
||||
|
||||
// SPECULAR texture
|
||||
if( oldMat.sTexSpecular.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
|
||||
if (oldMat.sTexSpecular.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexSpecular, aiTextureType_SPECULAR);
|
||||
|
||||
// OPACITY texture
|
||||
if( oldMat.sTexOpacity.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
|
||||
if (oldMat.sTexOpacity.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexOpacity, aiTextureType_OPACITY);
|
||||
|
||||
// EMISSIVE texture
|
||||
if( oldMat.sTexEmissive.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
|
||||
if (oldMat.sTexEmissive.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexEmissive, aiTextureType_EMISSIVE);
|
||||
|
||||
// BUMP texture
|
||||
if( oldMat.sTexBump.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
|
||||
if (oldMat.sTexBump.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexBump, aiTextureType_HEIGHT);
|
||||
|
||||
// SHININESS texture
|
||||
if( oldMat.sTexShininess.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
|
||||
if (oldMat.sTexShininess.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexShininess, aiTextureType_SHININESS);
|
||||
|
||||
// REFLECTION texture
|
||||
if( oldMat.sTexReflective.mMapName.length() > 0)
|
||||
CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
|
||||
if (oldMat.sTexReflective.mMapName.length() > 0)
|
||||
CopyTexture(mat, oldMat.sTexReflective, aiTextureType_REFLECTION);
|
||||
|
||||
// Store the name of the material itself, too
|
||||
if( oldMat.mName.length()) {
|
||||
if (oldMat.mName.length()) {
|
||||
aiString tex;
|
||||
tex.Set( oldMat.mName);
|
||||
mat.AddProperty( &tex, AI_MATKEY_NAME);
|
||||
tex.Set(oldMat.mName);
|
||||
mat.AddProperty(&tex, AI_MATKEY_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Split meshes by their materials and generate output aiMesh'es
|
||||
void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
||||
{
|
||||
std::vector<aiMesh*> avOutMeshes;
|
||||
void Discreet3DSImporter::ConvertMeshes(aiScene *pcOut) {
|
||||
std::vector<aiMesh *> avOutMeshes;
|
||||
avOutMeshes.reserve(mScene->mMeshes.size() * 2);
|
||||
|
||||
unsigned int iFaceCnt = 0,num = 0;
|
||||
unsigned int iFaceCnt = 0, num = 0;
|
||||
aiString name;
|
||||
|
||||
// we need to split all meshes by their materials
|
||||
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()]);
|
||||
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()]);
|
||||
|
||||
name.length = ASSIMP_itoa10(name.data,num++);
|
||||
name.length = ASSIMP_itoa10(name.data, num++);
|
||||
|
||||
unsigned int iNum = 0;
|
||||
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);
|
||||
}
|
||||
// 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()) {
|
||||
continue;
|
||||
}
|
||||
aiMesh* meshOut = new aiMesh();
|
||||
aiMesh *meshOut = new aiMesh();
|
||||
meshOut->mName = name;
|
||||
meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
||||
|
||||
|
@ -386,12 +360,12 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
|||
meshOut->mMaterialIndex = p;
|
||||
|
||||
// use the color data as temporary storage
|
||||
meshOut->mColors[0] = (aiColor4D*)(&*i);
|
||||
meshOut->mColors[0] = (aiColor4D *)(&*i);
|
||||
avOutMeshes.push_back(meshOut);
|
||||
|
||||
// convert vertices
|
||||
meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
|
||||
meshOut->mNumVertices = meshOut->mNumFaces*3;
|
||||
meshOut->mNumVertices = meshOut->mNumFaces * 3;
|
||||
|
||||
// allocate enough storage for faces
|
||||
meshOut->mFaces = new aiFace[meshOut->mNumFaces];
|
||||
|
@ -399,23 +373,20 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
|||
|
||||
meshOut->mVertices = 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];
|
||||
}
|
||||
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];
|
||||
aiFace& face = meshOut->mFaces[q];
|
||||
aiFace &face = meshOut->mFaces[q];
|
||||
|
||||
face.mIndices = new unsigned int[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];
|
||||
meshOut->mVertices[base] = (*i).mPositions[idx];
|
||||
meshOut->mNormals [base] = (*i).mNormals[idx];
|
||||
meshOut->mNormals[base] = (*i).mNormals[idx];
|
||||
|
||||
if ((*i).mTexCoords.size())
|
||||
meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
|
||||
|
@ -428,8 +399,8 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
|
|||
|
||||
// Copy them to the output array
|
||||
pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
|
||||
pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
|
||||
for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
|
||||
pcOut->mMeshes = new aiMesh *[pcOut->mNumMeshes]();
|
||||
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++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
|
||||
void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||
D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/)
|
||||
{
|
||||
void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
|
||||
D3DS::Node *pcIn, aiMatrix4x4 & /*absTrafo*/) {
|
||||
std::vector<unsigned int> iArray;
|
||||
iArray.reserve(3);
|
||||
|
||||
aiMatrix4x4 abs;
|
||||
|
||||
// Find all meshes with the same name as the node
|
||||
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
|
||||
{
|
||||
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
|
||||
ai_assert(NULL != pcMesh);
|
||||
for (unsigned int a = 0; a < pcSOut->mNumMeshes; ++a) {
|
||||
const D3DS::Mesh *pcMesh = (const D3DS::Mesh *)pcSOut->mMeshes[a]->mColors[0];
|
||||
ai_assert(nullptr != pcMesh);
|
||||
|
||||
if (pcIn->mName == pcMesh->mName)
|
||||
iArray.push_back(a);
|
||||
}
|
||||
if (!iArray.empty())
|
||||
{
|
||||
if (!iArray.empty()) {
|
||||
// The matrix should be identical for all meshes with the
|
||||
// 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
|
||||
// vertices back to their relative and local space
|
||||
aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
|
||||
mInv.Inverse();mInvTransposed.Transpose();
|
||||
mInv.Inverse();
|
||||
mInvTransposed.Transpose();
|
||||
aiVector3D pivot = pcIn->vPivot;
|
||||
|
||||
pcOut->mNumMeshes = (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];
|
||||
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
|
||||
// fixme: consider computing normals after this, so we don't need to transform them
|
||||
const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices;
|
||||
aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
||||
const aiVector3D *const pvEnd = mesh->mVertices + mesh->mNumVertices;
|
||||
aiVector3D *pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
||||
|
||||
for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
||||
*pvCurrent = mInv * (*pvCurrent);
|
||||
|
@ -489,8 +457,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
}
|
||||
|
||||
// Handle negative transformation matrix determinant -> invert vertex x
|
||||
if (imesh->mMat.Determinant() < 0.0f)
|
||||
{
|
||||
if (imesh->mMat.Determinant() < 0.0f) {
|
||||
/* we *must* have normals */
|
||||
for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
|
||||
pvCurrent->x *= -1.f;
|
||||
|
@ -500,17 +467,15 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
}
|
||||
|
||||
// Handle pivot point
|
||||
if (pivot.x || pivot.y || pivot.z)
|
||||
{
|
||||
if (pivot.x || pivot.y || pivot.z) {
|
||||
for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
|
||||
*pvCurrent -= pivot;
|
||||
}
|
||||
}
|
||||
|
||||
mesh->mColors[1] = (aiColor4D*)1;
|
||||
}
|
||||
else
|
||||
mesh->mColors[1] = (aiColor4D*)1;
|
||||
mesh->mColors[1] = (aiColor4D *)1;
|
||||
} else
|
||||
mesh->mColors[1] = (aiColor4D *)1;
|
||||
|
||||
// Setup the mesh index
|
||||
pcOut->mMeshes[i] = iIndex;
|
||||
|
@ -519,48 +484,48 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
|
||||
// Setup the name of the node
|
||||
// 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];
|
||||
ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
|
||||
std::string tempStr = pcIn->mName + "_inst_";
|
||||
tempStr += tmp;
|
||||
pcOut->mName.Set(tempStr);
|
||||
}
|
||||
else
|
||||
} else
|
||||
pcOut->mName.Set(pcIn->mName);
|
||||
|
||||
// Now build the transformation matrix of the node
|
||||
// ROTATION
|
||||
if (pcIn->aRotationKeys.size()){
|
||||
if (pcIn->aRotationKeys.size()) {
|
||||
|
||||
// FIX to get to Assimp's quaternion conventions
|
||||
for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
|
||||
(*it).mValue.w *= -1.f;
|
||||
}
|
||||
|
||||
pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
|
||||
}
|
||||
else if (pcIn->aCameraRollKeys.size())
|
||||
{
|
||||
aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
|
||||
pcOut->mTransformation = aiMatrix4x4(pcIn->aRotationKeys[0].mValue.GetMatrix());
|
||||
} else if (pcIn->aCameraRollKeys.size()) {
|
||||
aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(-pcIn->aCameraRollKeys[0].mValue),
|
||||
pcOut->mTransformation);
|
||||
}
|
||||
|
||||
// SCALING
|
||||
aiMatrix4x4& m = pcOut->mTransformation;
|
||||
if (pcIn->aScalingKeys.size())
|
||||
{
|
||||
const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
|
||||
m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
|
||||
m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
|
||||
m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
|
||||
aiMatrix4x4 &m = pcOut->mTransformation;
|
||||
if (pcIn->aScalingKeys.size()) {
|
||||
const aiVector3D &v = pcIn->aScalingKeys[0].mValue;
|
||||
m.a1 *= v.x;
|
||||
m.b1 *= v.x;
|
||||
m.c1 *= v.x;
|
||||
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
|
||||
if (pcIn->aPositionKeys.size())
|
||||
{
|
||||
const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
|
||||
if (pcIn->aPositionKeys.size()) {
|
||||
const aiVector3D &v = pcIn->aPositionKeys[0].mValue;
|
||||
m.a4 += v.x;
|
||||
m.b4 += v.y;
|
||||
m.c4 += v.z;
|
||||
|
@ -569,28 +534,25 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
// Generate animation channels for the node
|
||||
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
|
||||
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
|
||||
pcIn->aTargetPositionKeys.size() > 1)
|
||||
{
|
||||
aiAnimation* anim = pcSOut->mAnimations[0];
|
||||
pcIn->aTargetPositionKeys.size() > 1) {
|
||||
aiAnimation *anim = pcSOut->mAnimations[0];
|
||||
ai_assert(nullptr != anim);
|
||||
|
||||
if (pcIn->aCameraRollKeys.size() > 1)
|
||||
{
|
||||
if (pcIn->aCameraRollKeys.size() > 1) {
|
||||
ASSIMP_LOG_DEBUG("3DS: Converting camera roll track ...");
|
||||
|
||||
// Camera roll keys - in fact they're just rotations
|
||||
// around the camera's z axis. The angles are given
|
||||
// in degrees (and they're clockwise).
|
||||
pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
|
||||
for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
|
||||
{
|
||||
aiQuatKey& q = pcIn->aRotationKeys[i];
|
||||
aiFloatKey& f = pcIn->aCameraRollKeys[i];
|
||||
for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size(); ++i) {
|
||||
aiQuatKey &q = pcIn->aRotationKeys[i];
|
||||
aiFloatKey &f = pcIn->aCameraRollKeys[i];
|
||||
|
||||
q.mTime = f.mTime;
|
||||
|
||||
// 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
|
||||
|
@ -636,41 +598,38 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
// 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
|
||||
// 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) {
|
||||
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) {
|
||||
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
|
||||
aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
|
||||
aiNodeAnim *nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
|
||||
nda->mNodeName.Set(pcIn->mName);
|
||||
|
||||
// POSITION keys
|
||||
if (pcIn->aPositionKeys.size() > 0)
|
||||
{
|
||||
if (pcIn->aPositionKeys.size() > 0) {
|
||||
nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
|
||||
nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
|
||||
::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
|
||||
sizeof(aiVectorKey)*nda->mNumPositionKeys);
|
||||
::memcpy(nda->mPositionKeys, &pcIn->aPositionKeys[0],
|
||||
sizeof(aiVectorKey) * nda->mNumPositionKeys);
|
||||
}
|
||||
|
||||
// ROTATION keys
|
||||
if (pcIn->aRotationKeys.size() > 0)
|
||||
{
|
||||
if (pcIn->aRotationKeys.size() > 0) {
|
||||
nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
|
||||
nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
|
||||
|
||||
// Rotations are quaternion offsets
|
||||
aiQuaternion abs1;
|
||||
for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
|
||||
{
|
||||
const aiQuatKey& q = pcIn->aRotationKeys[n];
|
||||
for (unsigned int n = 0; n < nda->mNumRotationKeys; ++n) {
|
||||
const aiQuatKey &q = pcIn->aRotationKeys[n];
|
||||
|
||||
abs1 = (n ? abs1 * q.mValue : q.mValue);
|
||||
nda->mRotationKeys[n].mTime = q.mTime;
|
||||
|
@ -679,59 +638,53 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
}
|
||||
|
||||
// SCALING keys
|
||||
if (pcIn->aScalingKeys.size() > 0)
|
||||
{
|
||||
if (pcIn->aScalingKeys.size() > 0) {
|
||||
nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
|
||||
nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
|
||||
::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
|
||||
sizeof(aiVectorKey)*nda->mNumScalingKeys);
|
||||
::memcpy(nda->mScalingKeys, &pcIn->aScalingKeys[0],
|
||||
sizeof(aiVectorKey) * nda->mNumScalingKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate storage for children
|
||||
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
|
||||
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]->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
|
||||
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
|
||||
// this is rather easy here.
|
||||
|
||||
if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
|
||||
node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
|
||||
node->aTargetPositionKeys.size() > 1)
|
||||
{
|
||||
node->aTargetPositionKeys.size() > 1) {
|
||||
++cnt;
|
||||
|
||||
// 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
|
||||
for (unsigned int i = 0; i < node->mChildren.size();++i)
|
||||
CountTracks(node->mChildren[i],cnt);
|
||||
for (unsigned int i = 0; i < node->mChildren.size(); ++i)
|
||||
CountTracks(node->mChildren[i], cnt);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Generate the output node graph
|
||||
void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
||||
{
|
||||
void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
|
||||
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.
|
||||
// generate a flat hiearachy which looks like this:
|
||||
|
@ -747,27 +700,25 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
|||
pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
|
||||
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>");
|
||||
|
||||
// Build dummy nodes for all meshes
|
||||
unsigned int a = 0;
|
||||
for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
|
||||
{
|
||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
for (unsigned int i = 0; i < pcOut->mNumMeshes; ++i, ++a) {
|
||||
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
pcNode->mParent = pcOut->mRootNode;
|
||||
pcNode->mMeshes = new unsigned int[1];
|
||||
pcNode->mMeshes[0] = i;
|
||||
pcNode->mNumMeshes = 1;
|
||||
|
||||
// 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
|
||||
for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
|
||||
{
|
||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
for (unsigned int i = 0; i < (unsigned int)mScene->mCameras.size(); ++i, ++a) {
|
||||
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
pcNode->mParent = pcOut->mRootNode;
|
||||
|
||||
// Build a name for the node
|
||||
|
@ -775,75 +726,68 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
|
|||
}
|
||||
|
||||
// Build dummy nodes for all lights
|
||||
for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
|
||||
{
|
||||
aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
for (unsigned int i = 0; i < (unsigned int)mScene->mLights.size(); ++i, ++a) {
|
||||
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
|
||||
pcNode->mParent = pcOut->mRootNode;
|
||||
|
||||
// Build a name for the node
|
||||
pcNode->mName = mScene->mLights[i]->mName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// First of all: find out how many scaling, rotation and translation
|
||||
// animation tracks we'll have afterwards
|
||||
unsigned int numChannel = 0;
|
||||
CountTracks(mRootNode,numChannel);
|
||||
CountTracks(mRootNode, numChannel);
|
||||
|
||||
if (numChannel)
|
||||
{
|
||||
if (numChannel) {
|
||||
// Allocate a primary animation channel
|
||||
pcOut->mNumAnimations = 1;
|
||||
pcOut->mAnimations = new aiAnimation*[1];
|
||||
aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
|
||||
pcOut->mAnimations = new aiAnimation *[1];
|
||||
aiAnimation *anim = pcOut->mAnimations[0] = new aiAnimation();
|
||||
|
||||
anim->mName.Set("3DSMasterAnim");
|
||||
|
||||
// Allocate enough storage for all node animation channels,
|
||||
// but don't set the mNumChannels member - we'll use it to
|
||||
// index into the array
|
||||
anim->mChannels = new aiNodeAnim*[numChannel];
|
||||
anim->mChannels = new aiNodeAnim *[numChannel];
|
||||
}
|
||||
|
||||
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
|
||||
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a)
|
||||
{
|
||||
pcOut->mMeshes[a]->mColors[0] = NULL;
|
||||
pcOut->mMeshes[a]->mColors[1] = NULL;
|
||||
for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
|
||||
pcOut->mMeshes[a]->mColors[0] = nullptr;
|
||||
pcOut->mMeshes[a]->mColors[1] = nullptr;
|
||||
}
|
||||
|
||||
pcOut->mRootNode->mTransformation = aiMatrix4x4(
|
||||
1.f,0.f,0.f,0.f,
|
||||
0.f,0.f,1.f,0.f,
|
||||
0.f,-1.f,0.f,0.f,
|
||||
0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
|
||||
1.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
0.f, -1.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f) *
|
||||
pcOut->mRootNode->mTransformation;
|
||||
|
||||
// If the root node is unnamed name it "<3DSRoot>"
|
||||
if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
|
||||
(pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
|
||||
{
|
||||
if (::strstr(pcOut->mRootNode->mName.data, "UNNAMED") ||
|
||||
(pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')) {
|
||||
pcOut->mRootNode->mName.Set("<3DSRoot>");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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
|
||||
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
|
||||
for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
|
||||
{
|
||||
aiMaterial* pcNew = new aiMaterial();
|
||||
ConvertMaterial(mScene->mMaterials[i],*pcNew);
|
||||
for (unsigned int i = 0; i < pcOut->mNumMaterials; ++i) {
|
||||
aiMaterial *pcNew = new aiMaterial();
|
||||
ConvertMaterial(mScene->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
|
||||
pcOut->mNumLights = (unsigned int)mScene->mLights.size();
|
||||
if (pcOut->mNumLights)
|
||||
{
|
||||
pcOut->mLights = new aiLight*[pcOut->mNumLights];
|
||||
::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
|
||||
if (pcOut->mNumLights) {
|
||||
pcOut->mLights = new aiLight *[pcOut->mNumLights];
|
||||
::memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
|
||||
}
|
||||
|
||||
// Now copy all cameras to the output scene
|
||||
pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
|
||||
if (pcOut->mNumCameras)
|
||||
{
|
||||
pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
|
||||
::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
|
||||
if (pcOut->mNumCameras) {
|
||||
pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
|
||||
::memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,16 +43,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
|
||||
|
||||
#include "3DS/3DSExporter.h"
|
||||
#include "3DS/3DSLoader.h"
|
||||
#include "3DS/3DSHelper.h"
|
||||
#include "AssetLib/3DS/3DSExporter.h"
|
||||
#include "AssetLib/3DS/3DSHelper.h"
|
||||
#include "AssetLib/3DS/3DSLoader.h"
|
||||
#include "PostProcessing/SplitLargeMeshes.h"
|
||||
|
||||
#include <assimp/SceneCombiner.h>
|
||||
#include <assimp/StringComparison.h>
|
||||
#include <assimp/IOSystem.hpp>
|
||||
#include <assimp/DefaultLogger.hpp>
|
||||
#include <assimp/Exporter.hpp>
|
||||
#include <assimp/IOSystem.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -62,22 +62,21 @@ using namespace D3DS;
|
|||
|
||||
namespace {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Scope utility to write a 3DS file chunk.
|
||||
//
|
||||
// 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
|
||||
// size based on the then-position of the output stream cursor is filled in.
|
||||
class ChunkWriter {
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Scope utility to write a 3DS file chunk.
|
||||
//
|
||||
// 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
|
||||
// size based on the then-position of the output stream cursor is filled in.
|
||||
class ChunkWriter {
|
||||
enum {
|
||||
CHUNK_SIZE_NOT_SET = 0xdeadbeef
|
||||
, SIZE_OFFSET = 2
|
||||
CHUNK_SIZE_NOT_SET = 0xdeadbeef,
|
||||
SIZE_OFFSET = 2
|
||||
};
|
||||
public:
|
||||
|
||||
ChunkWriter(StreamWriterLE& writer, uint16_t chunk_type)
|
||||
: writer(writer)
|
||||
{
|
||||
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);
|
||||
|
@ -94,18 +93,17 @@ namespace {
|
|||
writer.SetCurrentPos(head_pos);
|
||||
}
|
||||
|
||||
private:
|
||||
StreamWriterLE& writer;
|
||||
private:
|
||||
StreamWriterLE &writer;
|
||||
std::size_t chunk_start_pos;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 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) {
|
||||
// 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};
|
||||
char postfix[10] = { 0 };
|
||||
ASSIMP_itoa10(postfix, index);
|
||||
|
||||
std::string result = node.mName.C_Str();
|
||||
|
@ -113,14 +111,14 @@ namespace {
|
|||
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) {
|
||||
// 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};
|
||||
char postfix[10] = { 0 };
|
||||
ASSIMP_itoa10(postfix, index);
|
||||
|
||||
aiString mat_name;
|
||||
|
@ -129,34 +127,33 @@ namespace {
|
|||
}
|
||||
|
||||
return "Material" + underscore + postfix;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect world transformations for each node
|
||||
void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
|
||||
const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
|
||||
// Collect world transformations for each node
|
||||
void CollectTrafos(const aiNode *node, std::map<const aiNode *, aiMatrix4x4> &trafos) {
|
||||
const aiMatrix4x4 &parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
|
||||
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) {
|
||||
// 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
|
||||
void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
|
||||
{
|
||||
std::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
|
||||
if(!outfile) {
|
||||
void ExportScene3DS(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
|
||||
std::shared_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
|
||||
if (!outfile) {
|
||||
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
|
||||
// which is not possible with the current way of specifying preprocess steps
|
||||
// in |Exporter::ExportFormatEntry|.
|
||||
aiScene* scenecopy_tmp;
|
||||
SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
|
||||
aiScene *scenecopy_tmp;
|
||||
SceneCombiner::CopyScene(&scenecopy_tmp, pScene);
|
||||
std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
|
||||
|
||||
SplitLargeMeshesProcess_Triangle tri_splitter;
|
||||
|
@ -186,10 +183,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
|
|||
} // end of namespace Assimp
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* scene)
|
||||
: scene(scene)
|
||||
, writer(outfile)
|
||||
{
|
||||
Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene *scene) :
|
||||
scene(scene), writer(outfile) {
|
||||
CollectTrafos(scene->mRootNode, trafos);
|
||||
CollectMeshes(scene->mRootNode, meshes);
|
||||
|
||||
|
@ -217,10 +212,8 @@ Discreet3DSExporter::~Discreet3DSExporter() {
|
|||
// 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
|
||||
{
|
||||
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);
|
||||
if (sibling_level != -1) {
|
||||
hierarchy_pos =(uint16_t) sibling_level;
|
||||
hierarchy_pos = (uint16_t)sibling_level;
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
{
|
||||
|
@ -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) {
|
||||
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);
|
||||
const std::string& name = GetMaterialName(mat, i);
|
||||
const std::string &name = GetMaterialName(mat, i);
|
||||
WriteString(name);
|
||||
}
|
||||
|
||||
|
@ -314,7 +306,7 @@ void Discreet3DSExporter::WriteMaterials()
|
|||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
|
||||
|
||||
Discreet3DS::shadetype3ds shading_mode_out;
|
||||
switch(shading_mode) {
|
||||
switch (shading_mode) {
|
||||
case aiShadingMode_Flat:
|
||||
case aiShadingMode_NoShading:
|
||||
shading_mode_out = Discreet3DS::Flat;
|
||||
|
@ -341,7 +333,6 @@ void Discreet3DSExporter::WriteMaterials()
|
|||
writer.PutU2(static_cast<uint16_t>(shading_mode_out));
|
||||
}
|
||||
|
||||
|
||||
float f;
|
||||
if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
|
||||
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;
|
||||
aiTextureMapMode map_mode[2] = {
|
||||
aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
|
||||
|
@ -400,8 +390,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
|
|||
uint16_t val = 0; // WRAP
|
||||
if (map_mode[0] == aiTextureMapMode_Mirror) {
|
||||
val = 0x2;
|
||||
}
|
||||
else if (map_mode[0] == aiTextureMapMode_Decal) {
|
||||
} else if (map_mode[0] == aiTextureMapMode_Decal) {
|
||||
val = 0x10;
|
||||
}
|
||||
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:
|
||||
// i) not all importers support reading them
|
||||
// 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
|
||||
// appear correctly if importers don't read the scene hierarchy at all.
|
||||
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 aiMesh& mesh = *scene->mMeshes[mesh_idx];
|
||||
const aiMesh &mesh = *scene->mMeshes[mesh_idx];
|
||||
|
||||
// This should not happen if the SLM step is correctly executed
|
||||
// before the scene is handed to the exporter
|
||||
ai_assert(mesh.mNumVertices <= 0xffff);
|
||||
ai_assert(mesh.mNumFaces <= 0xffff);
|
||||
|
||||
const aiMatrix4x4& trafo = trafos[&node];
|
||||
const aiMatrix4x4 &trafo = trafos[&node];
|
||||
|
||||
ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// TRIMESH chunk
|
||||
ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
|
||||
|
||||
|
@ -452,7 +439,7 @@ void Discreet3DSExporter::WriteMeshes()
|
|||
const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
|
||||
writer.PutU2(count);
|
||||
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.y);
|
||||
writer.PutF4(v.z);
|
||||
|
@ -466,7 +453,7 @@ void Discreet3DSExporter::WriteMeshes()
|
|||
writer.PutU2(count);
|
||||
|
||||
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.y);
|
||||
}
|
||||
|
@ -481,7 +468,7 @@ void Discreet3DSExporter::WriteMeshes()
|
|||
// Count triangles, discard lines and points
|
||||
uint16_t count = 0;
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
@ -492,7 +479,7 @@ void Discreet3DSExporter::WriteMeshes()
|
|||
|
||||
writer.PutU2(count);
|
||||
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) {
|
||||
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);
|
||||
const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
|
||||
const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
|
||||
WriteString(name);
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
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);
|
||||
writer.PutF4(color.r);
|
||||
writer.PutF4(color.g);
|
||||
|
@ -577,6 +563,5 @@ void Discreet3DSExporter::WritePercentChunk(double f) {
|
|||
writer.PutF8(f);
|
||||
}
|
||||
|
||||
|
||||
#endif // ASSIMP_BUILD_NO_3DS_EXPORTER
|
||||
#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 <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/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 "D3MFOpcPackage.h"
|
||||
|
||||
#ifdef ASSIMP_USE_HUNTER
|
||||
# include <zip/zip.h>
|
||||
#include <zip/zip.h>
|
||||
#else
|
||||
# include <contrib/zip/src/zip.h>
|
||||
#include <contrib/zip/src/zip.h>
|
||||
#endif
|
||||
|
||||
namespace Assimp {
|
||||
|
||||
void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
|
||||
if ( nullptr == pIOSystem ) {
|
||||
throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
|
||||
void ExportScene3MF(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
|
||||
if (nullptr == pIOSystem) {
|
||||
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||||
}
|
||||
D3MF::D3MFExporter myExporter( pFile, pScene );
|
||||
if ( myExporter.validate() ) {
|
||||
if ( pIOSystem->Exists( pFile ) ) {
|
||||
if ( !pIOSystem->DeleteFile( pFile ) ) {
|
||||
throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
|
||||
D3MF::D3MFExporter myExporter(pFile, pScene);
|
||||
if (myExporter.validate()) {
|
||||
if (pIOSystem->Exists(pFile)) {
|
||||
if (!pIOSystem->DeleteFile(pFile)) {
|
||||
throw DeadlyExportError("File exists, cannot override : " + std::string(pFile));
|
||||
}
|
||||
}
|
||||
bool ok = myExporter.exportArchive(pFile);
|
||||
if ( !ok ) {
|
||||
throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
|
||||
if (!ok) {
|
||||
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace D3MF {
|
||||
|
||||
D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
|
||||
: mArchiveName( pFile )
|
||||
, m_zipArchive( nullptr )
|
||||
, mScene( pScene )
|
||||
, mModelOutput()
|
||||
, mRelOutput()
|
||||
, mContentOutput()
|
||||
, mBuildItems()
|
||||
, mRelations() {
|
||||
D3MFExporter::D3MFExporter(const char *pFile, const aiScene *pScene) :
|
||||
mArchiveName(pFile), m_zipArchive(nullptr), mScene(pScene), mModelOutput(), mRelOutput(), mContentOutput(), mBuildItems(), mRelations() {
|
||||
// empty
|
||||
}
|
||||
|
||||
D3MFExporter::~D3MFExporter() {
|
||||
for ( size_t i = 0; i < mRelations.size(); ++i ) {
|
||||
delete mRelations[ i ];
|
||||
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||||
delete mRelations[i];
|
||||
}
|
||||
mRelations.clear();
|
||||
}
|
||||
|
||||
bool D3MFExporter::validate() {
|
||||
if ( mArchiveName.empty() ) {
|
||||
if (mArchiveName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr == mScene ) {
|
||||
if (nullptr == mScene) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3MFExporter::exportArchive( const char *file ) {
|
||||
bool ok( true );
|
||||
bool D3MFExporter::exportArchive(const char *file) {
|
||||
bool ok(true);
|
||||
|
||||
m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
|
||||
if ( nullptr == m_zipArchive ) {
|
||||
m_zipArchive = zip_open(file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
|
||||
if (nullptr == m_zipArchive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -126,7 +119,7 @@ bool D3MFExporter::exportArchive( const char *file ) {
|
|||
ok |= export3DModel();
|
||||
ok |= exportRelations();
|
||||
|
||||
zip_close( m_zipArchive );
|
||||
zip_close(m_zipArchive);
|
||||
m_zipArchive = nullptr;
|
||||
|
||||
return ok;
|
||||
|
@ -145,7 +138,7 @@ bool D3MFExporter::exportContentTypes() {
|
|||
mContentOutput << std::endl;
|
||||
mContentOutput << "</Types>";
|
||||
mContentOutput << std::endl;
|
||||
exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
|
||||
exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -157,20 +150,20 @@ bool D3MFExporter::exportRelations() {
|
|||
mRelOutput << std::endl;
|
||||
mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
|
||||
|
||||
for ( size_t i = 0; i < mRelations.size(); ++i ) {
|
||||
if ( mRelations[ i ]->target[ 0 ] == '/' ) {
|
||||
mRelOutput << "<Relationship Target=\"" << mRelations[ i ]->target << "\" ";
|
||||
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||||
if (mRelations[i]->target[0] == '/') {
|
||||
mRelOutput << "<Relationship Target=\"" << mRelations[i]->target << "\" ";
|
||||
} else {
|
||||
mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
|
||||
mRelOutput << "<Relationship Target=\"/" << mRelations[i]->target << "\" ";
|
||||
}
|
||||
mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
|
||||
mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
|
||||
mRelOutput << "Type=\"" << mRelations[i]->type << "\" />";
|
||||
mRelOutput << std::endl;
|
||||
}
|
||||
mRelOutput << "</Relationships>";
|
||||
mRelOutput << std::endl;
|
||||
|
||||
writeRelInfoToFile( "_rels", ".rels" );
|
||||
writeRelInfoToFile("_rels", ".rels");
|
||||
mRelOutput.flush();
|
||||
|
||||
return true;
|
||||
|
@ -192,7 +185,6 @@ bool D3MFExporter::export3DModel() {
|
|||
|
||||
writeObjects();
|
||||
|
||||
|
||||
mModelOutput << "</" << XmlTag::resources << ">";
|
||||
mModelOutput << std::endl;
|
||||
writeBuild();
|
||||
|
@ -203,9 +195,9 @@ bool D3MFExporter::export3DModel() {
|
|||
info->id = "rel0";
|
||||
info->target = "/3D/3DModel.model";
|
||||
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();
|
||||
|
||||
return true;
|
||||
|
@ -217,22 +209,22 @@ void D3MFExporter::writeHeader() {
|
|||
}
|
||||
|
||||
void D3MFExporter::writeMetaData() {
|
||||
if ( nullptr == mScene->mMetaData ) {
|
||||
if (nullptr == mScene->mMetaData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
|
||||
if ( 0 == numMetaEntries ) {
|
||||
const unsigned int numMetaEntries(mScene->mMetaData->mNumProperties);
|
||||
if (0 == numMetaEntries) {
|
||||
return;
|
||||
}
|
||||
|
||||
const aiString *key = nullptr;
|
||||
const aiMetadataEntry *entry(nullptr);
|
||||
for ( size_t i = 0; i < numMetaEntries; ++i ) {
|
||||
mScene->mMetaData->Get( i, key, entry );
|
||||
std::string k( key->C_Str() );
|
||||
for (size_t i = 0; i < numMetaEntries; ++i) {
|
||||
mScene->mMetaData->Get(i, key, entry);
|
||||
std::string k(key->C_Str());
|
||||
aiString value;
|
||||
mScene->mMetaData->Get( k, value );
|
||||
mScene->mMetaData->Get(k, value);
|
||||
mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
|
||||
mModelOutput << value.C_Str();
|
||||
mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
|
||||
|
@ -241,33 +233,32 @@ void D3MFExporter::writeMetaData() {
|
|||
|
||||
void D3MFExporter::writeBaseMaterials() {
|
||||
mModelOutput << "<basematerials id=\"1\">\n";
|
||||
std::string strName, hexDiffuseColor , tmp;
|
||||
for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {
|
||||
aiMaterial *mat = mScene->mMaterials[ i ];
|
||||
std::string strName, hexDiffuseColor, tmp;
|
||||
for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
|
||||
aiMaterial *mat = mScene->mMaterials[i];
|
||||
aiString name;
|
||||
if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) {
|
||||
strName = "basemat_" + to_string( i );
|
||||
if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
|
||||
strName = "basemat_" + to_string(i);
|
||||
} else {
|
||||
strName = name.C_Str();
|
||||
}
|
||||
aiColor4D color;
|
||||
if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) {
|
||||
if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) {
|
||||
hexDiffuseColor.clear();
|
||||
tmp.clear();
|
||||
// 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(
|
||||
(int)((ai_real)color.r)*255,
|
||||
(int)((ai_real)color.g)*255,
|
||||
(int)((ai_real)color.b)*255,
|
||||
(int)((ai_real)color.a)*255,
|
||||
true
|
||||
);
|
||||
(int)((ai_real)color.r) * 255,
|
||||
(int)((ai_real)color.g) * 255,
|
||||
(int)((ai_real)color.b) * 255,
|
||||
(int)((ai_real)color.a) * 255,
|
||||
true);
|
||||
|
||||
}else{
|
||||
} else {
|
||||
hexDiffuseColor = "#";
|
||||
tmp = DecimalToHexa( (ai_real) color.r );
|
||||
tmp = DecimalToHexa((ai_real)color.r);
|
||||
hexDiffuseColor += tmp;
|
||||
tmp = DecimalToHexa((ai_real)color.g);
|
||||
hexDiffuseColor += tmp;
|
||||
|
@ -280,76 +271,76 @@ void D3MFExporter::writeBaseMaterials() {
|
|||
hexDiffuseColor = "#FFFFFFFF";
|
||||
}
|
||||
|
||||
mModelOutput << "<base name=\""+strName+"\" "+" displaycolor=\""+hexDiffuseColor+"\" />\n";
|
||||
mModelOutput << "<base name=\"" + strName + "\" " + " displaycolor=\"" + hexDiffuseColor + "\" />\n";
|
||||
}
|
||||
mModelOutput << "</basematerials>\n";
|
||||
}
|
||||
|
||||
void D3MFExporter::writeObjects() {
|
||||
if ( nullptr == mScene->mRootNode ) {
|
||||
if (nullptr == mScene->mRootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
aiNode *root = mScene->mRootNode;
|
||||
for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
|
||||
aiNode *currentNode( root->mChildren[ i ] );
|
||||
if ( nullptr == currentNode ) {
|
||||
for (unsigned int i = 0; i < root->mNumChildren; ++i) {
|
||||
aiNode *currentNode(root->mChildren[i]);
|
||||
if (nullptr == currentNode) {
|
||||
continue;
|
||||
}
|
||||
mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
|
||||
mModelOutput << std::endl;
|
||||
for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
|
||||
aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
|
||||
if ( nullptr == currentMesh ) {
|
||||
for (unsigned int j = 0; j < currentNode->mNumMeshes; ++j) {
|
||||
aiMesh *currentMesh = mScene->mMeshes[currentNode->mMeshes[j]];
|
||||
if (nullptr == currentMesh) {
|
||||
continue;
|
||||
}
|
||||
writeMesh( currentMesh );
|
||||
writeMesh(currentMesh);
|
||||
}
|
||||
mBuildItems.push_back( i );
|
||||
mBuildItems.push_back(i);
|
||||
|
||||
mModelOutput << "</" << XmlTag::object << ">";
|
||||
mModelOutput << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void D3MFExporter::writeMesh( aiMesh *mesh ) {
|
||||
if ( nullptr == mesh ) {
|
||||
void D3MFExporter::writeMesh(aiMesh *mesh) {
|
||||
if (nullptr == mesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
|
||||
mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
|
||||
for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
|
||||
writeVertex( mesh->mVertices[ i ] );
|
||||
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||||
writeVertex(mesh->mVertices[i]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 << std::endl;
|
||||
}
|
||||
|
||||
void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
|
||||
if ( nullptr == mesh ) {
|
||||
void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) {
|
||||
if (nullptr == mesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !mesh->HasFaces() ) {
|
||||
if (!mesh->HasFaces()) {
|
||||
return;
|
||||
}
|
||||
mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
|
||||
for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
|
||||
aiFace ¤tFace = mesh->mFaces[ i ];
|
||||
mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
|
||||
<< currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ]
|
||||
<< "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />";
|
||||
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
|
||||
aiFace ¤tFace = mesh->mFaces[i];
|
||||
mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\""
|
||||
<< currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2]
|
||||
<< "\" pid=\"1\" p1=\"" + to_string(matIdx) + "\" />";
|
||||
mModelOutput << std::endl;
|
||||
}
|
||||
mModelOutput << "</" << XmlTag::triangles << ">";
|
||||
|
@ -359,7 +350,7 @@ void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
|
|||
void D3MFExporter::writeBuild() {
|
||||
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 << std::endl;
|
||||
}
|
||||
|
@ -367,46 +358,45 @@ void D3MFExporter::writeBuild() {
|
|||
mModelOutput << std::endl;
|
||||
}
|
||||
|
||||
void D3MFExporter::exportContentTyp( const std::string &filename ) {
|
||||
if ( nullptr == m_zipArchive ) {
|
||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
||||
void D3MFExporter::exportContentTyp(const std::string &filename) {
|
||||
if (nullptr == m_zipArchive) {
|
||||
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||
}
|
||||
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() );
|
||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
||||
const std::string &exportTxt(mContentOutput.str());
|
||||
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 ) {
|
||||
if ( nullptr == m_zipArchive ) {
|
||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
||||
void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) {
|
||||
if (nullptr == m_zipArchive) {
|
||||
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||
}
|
||||
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() );
|
||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
||||
const std::string &exportTxt(mModelOutput.str());
|
||||
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 ) {
|
||||
if ( nullptr == m_zipArchive ) {
|
||||
throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
|
||||
void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) {
|
||||
if (nullptr == m_zipArchive) {
|
||||
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||||
}
|
||||
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() );
|
||||
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
|
||||
const std::string &exportTxt(mRelOutput.str());
|
||||
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||||
|
||||
zip_entry_close( m_zipArchive );
|
||||
zip_entry_close(m_zipArchive);
|
||||
}
|
||||
|
||||
|
||||
} // Namespace D3MF
|
||||
} // Namespace Assimp
|
||||
|
|
@ -44,24 +44,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#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/StringUtils.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 <vector>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "D3MFOpcPackage.h"
|
||||
#include <assimp/irrXMLWrapper.h>
|
||||
#include "3MFXmlTags.h"
|
||||
#include "D3MFOpcPackage.h"
|
||||
#include <assimp/fast_atof.h>
|
||||
#include <assimp/irrXMLWrapper.h>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
|
@ -70,15 +70,15 @@ namespace D3MF {
|
|||
|
||||
class XmlSerializer {
|
||||
public:
|
||||
using MatArray = std::vector<aiMaterial*>;
|
||||
using MatArray = std::vector<aiMaterial *>;
|
||||
using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
|
||||
|
||||
XmlSerializer(XmlReader* xmlReader)
|
||||
: mMeshes()
|
||||
, mMatArray()
|
||||
, mActiveMatGroup( 99999999 )
|
||||
, mMatId2MatArray()
|
||||
, xmlReader(xmlReader){
|
||||
XmlSerializer(XmlReader *xmlReader) :
|
||||
mMeshes(),
|
||||
mMatArray(),
|
||||
mActiveMatGroup(99999999),
|
||||
mMatId2MatArray(),
|
||||
xmlReader(xmlReader) {
|
||||
// empty
|
||||
}
|
||||
|
||||
|
@ -86,74 +86,74 @@ public:
|
|||
// empty
|
||||
}
|
||||
|
||||
void ImportXml(aiScene* scene) {
|
||||
if ( nullptr == scene ) {
|
||||
void ImportXml(aiScene *scene) {
|
||||
if (nullptr == scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
scene->mRootNode = new aiNode();
|
||||
std::vector<aiNode*> children;
|
||||
std::vector<aiNode *> children;
|
||||
|
||||
std::string nodeName;
|
||||
while(ReadToEndElement(D3MF::XmlTag::model)) {
|
||||
while (ReadToEndElement(D3MF::XmlTag::model)) {
|
||||
nodeName = xmlReader->getNodeName();
|
||||
if( nodeName == D3MF::XmlTag::object) {
|
||||
if (nodeName == D3MF::XmlTag::object) {
|
||||
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();
|
||||
} else if ( nodeName == D3MF::XmlTag::meta ) {
|
||||
} else if (nodeName == D3MF::XmlTag::meta) {
|
||||
ReadMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
if ( scene->mRootNode->mName.length == 0 ) {
|
||||
scene->mRootNode->mName.Set( "3MF" );
|
||||
if (scene->mRootNode->mName.length == 0) {
|
||||
scene->mRootNode->mName.Set("3MF");
|
||||
}
|
||||
|
||||
// import the metadata
|
||||
if ( !mMetaData.empty() ) {
|
||||
const size_t numMeta( mMetaData.size() );
|
||||
scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>( numMeta ) );
|
||||
for ( size_t i = 0; i < numMeta; ++i ) {
|
||||
aiString val( mMetaData[ i ].value );
|
||||
scene->mMetaData->Set(static_cast<unsigned int>( i ), mMetaData[ i ].name, val );
|
||||
if (!mMetaData.empty()) {
|
||||
const size_t numMeta(mMetaData.size());
|
||||
scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
|
||||
for (size_t i = 0; i < numMeta; ++i) {
|
||||
aiString val(mMetaData[i].value);
|
||||
scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
|
||||
}
|
||||
}
|
||||
|
||||
// import the meshes
|
||||
scene->mNumMeshes = static_cast<unsigned int>( mMeshes.size());
|
||||
scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
|
||||
std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes);
|
||||
scene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
|
||||
scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
|
||||
std::copy(mMeshes.begin(), mMeshes.end(), scene->mMeshes);
|
||||
|
||||
// import the materials
|
||||
scene->mNumMaterials = static_cast<unsigned int>( mMatArray.size() );
|
||||
if ( 0 != scene->mNumMaterials ) {
|
||||
scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ];
|
||||
std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials );
|
||||
scene->mNumMaterials = static_cast<unsigned int>(mMatArray.size());
|
||||
if (0 != scene->mNumMaterials) {
|
||||
scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
|
||||
std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials);
|
||||
}
|
||||
|
||||
// create the scenegraph
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
aiNode* ReadObject(aiScene* scene) {
|
||||
aiNode *ReadObject(aiScene *scene) {
|
||||
std::unique_ptr<aiNode> node(new aiNode());
|
||||
|
||||
std::vector<unsigned long> meshIds;
|
||||
|
||||
const char *attrib( nullptr );
|
||||
const char *attrib(nullptr);
|
||||
std::string name, type;
|
||||
attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() );
|
||||
if ( nullptr != attrib ) {
|
||||
attrib = xmlReader->getAttributeValue(D3MF::XmlTag::id.c_str());
|
||||
if (nullptr != attrib) {
|
||||
name = attrib;
|
||||
}
|
||||
attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() );
|
||||
if ( nullptr != attrib ) {
|
||||
attrib = xmlReader->getAttributeValue(D3MF::XmlTag::type.c_str());
|
||||
if (nullptr != attrib) {
|
||||
type = attrib;
|
||||
}
|
||||
|
||||
|
@ -162,8 +162,8 @@ private:
|
|||
|
||||
size_t meshIdx = mMeshes.size();
|
||||
|
||||
while(ReadToEndElement(D3MF::XmlTag::object)) {
|
||||
if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
|
||||
while (ReadToEndElement(D3MF::XmlTag::object)) {
|
||||
if (xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
|
||||
auto mesh = ReadMesh();
|
||||
|
||||
mesh->mName.Set(name);
|
||||
|
@ -183,11 +183,11 @@ private:
|
|||
}
|
||||
|
||||
aiMesh *ReadMesh() {
|
||||
aiMesh* mesh = new aiMesh();
|
||||
while(ReadToEndElement(D3MF::XmlTag::mesh)) {
|
||||
if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
|
||||
aiMesh *mesh = new aiMesh();
|
||||
while (ReadToEndElement(D3MF::XmlTag::mesh)) {
|
||||
if (xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
|
||||
ImportVertices(mesh);
|
||||
} else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
|
||||
} else if (xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
|
||||
ImportTriangles(mesh);
|
||||
}
|
||||
}
|
||||
|
@ -196,24 +196,24 @@ private:
|
|||
}
|
||||
|
||||
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();
|
||||
const std::string value = xmlReader->getNodeData();
|
||||
|
||||
if ( name.empty() ) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaEntry entry;
|
||||
entry.name = name;
|
||||
entry.value = value;
|
||||
mMetaData.push_back( entry );
|
||||
mMetaData.push_back(entry);
|
||||
}
|
||||
|
||||
void ImportVertices(aiMesh* mesh) {
|
||||
void ImportVertices(aiMesh *mesh) {
|
||||
std::vector<aiVector3D> vertices;
|
||||
while(ReadToEndElement(D3MF::XmlTag::vertices)) {
|
||||
if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
|
||||
while (ReadToEndElement(D3MF::XmlTag::vertices)) {
|
||||
if (xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
|
||||
vertices.push_back(ReadVertex());
|
||||
}
|
||||
}
|
||||
|
@ -233,16 +233,16 @@ private:
|
|||
return vertex;
|
||||
}
|
||||
|
||||
void ImportTriangles(aiMesh* mesh) {
|
||||
void ImportTriangles(aiMesh *mesh) {
|
||||
std::vector<aiFace> faces;
|
||||
|
||||
while(ReadToEndElement(D3MF::XmlTag::triangles)) {
|
||||
const std::string nodeName( xmlReader->getNodeName() );
|
||||
if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
|
||||
while (ReadToEndElement(D3MF::XmlTag::triangles)) {
|
||||
const std::string nodeName(xmlReader->getNodeName());
|
||||
if (xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
|
||||
faces.push_back(ReadTriangle());
|
||||
const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) );
|
||||
if ( nullptr != pidToken ) {
|
||||
int matIdx( std::atoi( pidToken ) );
|
||||
const char *pidToken(xmlReader->getAttributeValue(D3MF::XmlTag::p1.c_str()));
|
||||
if (nullptr != pidToken) {
|
||||
int matIdx(std::atoi(pidToken));
|
||||
mesh->mMaterialIndex = matIdx;
|
||||
}
|
||||
}
|
||||
|
@ -269,117 +269,115 @@ private:
|
|||
|
||||
void ReadBaseMaterials() {
|
||||
std::vector<unsigned int> MatIdArray;
|
||||
const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) );
|
||||
if ( nullptr != baseMaterialId ) {
|
||||
unsigned int id = std::atoi( baseMaterialId );
|
||||
const size_t newMatIdx( mMatArray.size() );
|
||||
if ( id != mActiveMatGroup ) {
|
||||
const char *baseMaterialId(xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_id.c_str()));
|
||||
if (nullptr != baseMaterialId) {
|
||||
unsigned int id = std::atoi(baseMaterialId);
|
||||
const size_t newMatIdx(mMatArray.size());
|
||||
if (id != mActiveMatGroup) {
|
||||
mActiveMatGroup = id;
|
||||
MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) );
|
||||
if ( mMatId2MatArray.end() == it ) {
|
||||
MatId2MatArray::const_iterator it(mMatId2MatArray.find(id));
|
||||
if (mMatId2MatArray.end() == it) {
|
||||
MatIdArray.clear();
|
||||
mMatId2MatArray[ id ] = MatIdArray;
|
||||
mMatId2MatArray[id] = MatIdArray;
|
||||
} else {
|
||||
MatIdArray = it->second;
|
||||
}
|
||||
}
|
||||
MatIdArray.push_back( static_cast<unsigned int>( newMatIdx ) );
|
||||
mMatId2MatArray[ mActiveMatGroup ] = MatIdArray;
|
||||
MatIdArray.push_back(static_cast<unsigned int>(newMatIdx));
|
||||
mMatId2MatArray[mActiveMatGroup] = MatIdArray;
|
||||
}
|
||||
|
||||
while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) {
|
||||
mMatArray.push_back( readMaterialDef() );
|
||||
while (ReadToEndElement(D3MF::XmlTag::basematerials)) {
|
||||
mMatArray.push_back(readMaterialDef());
|
||||
}
|
||||
}
|
||||
|
||||
bool parseColor( const char *color, aiColor4D &diffuse ) {
|
||||
if ( nullptr == color ) {
|
||||
bool parseColor(const char *color, aiColor4D &diffuse) {
|
||||
if (nullptr == color) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
|
||||
const size_t len( strlen( color ) );
|
||||
if ( 9 != len && 7 != len) {
|
||||
const size_t len(strlen(color));
|
||||
if (9 != len && 7 != len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *buf( color );
|
||||
if ( '#' != *buf ) {
|
||||
const char *buf(color);
|
||||
if ('#' != *buf) {
|
||||
return false;
|
||||
}
|
||||
++buf;
|
||||
char comp[ 3 ] = { 0,0,'\0' };
|
||||
char comp[3] = { 0, 0, '\0' };
|
||||
|
||||
comp[ 0 ] = *buf;
|
||||
comp[0] = *buf;
|
||||
++buf;
|
||||
comp[ 1 ] = *buf;
|
||||
comp[1] = *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;
|
||||
comp[ 1 ] = *buf;
|
||||
comp[1] = *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;
|
||||
++buf;
|
||||
comp[ 1 ] = *buf;
|
||||
++buf;
|
||||
diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
|
||||
|
||||
if(7 == len)
|
||||
if (7 == len)
|
||||
return true;
|
||||
comp[ 0 ] = *buf;
|
||||
comp[0] = *buf;
|
||||
++buf;
|
||||
comp[ 1 ] = *buf;
|
||||
comp[1] = *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;
|
||||
}
|
||||
|
||||
void assignDiffuseColor( aiMaterial *mat ) {
|
||||
const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() );
|
||||
void assignDiffuseColor(aiMaterial *mat) {
|
||||
const char *color = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_displaycolor.c_str());
|
||||
aiColor4D diffuse;
|
||||
if ( parseColor( color, diffuse ) ) {
|
||||
mat->AddProperty<aiColor4D>( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
|
||||
if (parseColor(color, diffuse)) {
|
||||
mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||
}
|
||||
|
||||
}
|
||||
aiMaterial *readMaterialDef() {
|
||||
aiMaterial *mat( nullptr );
|
||||
const char *name( nullptr );
|
||||
const std::string nodeName( xmlReader->getNodeName() );
|
||||
if ( nodeName == D3MF::XmlTag::basematerials_base ) {
|
||||
name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() );
|
||||
aiMaterial *mat(nullptr);
|
||||
const char *name(nullptr);
|
||||
const std::string nodeName(xmlReader->getNodeName());
|
||||
if (nodeName == D3MF::XmlTag::basematerials_base) {
|
||||
name = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_name.c_str());
|
||||
std::string stdMatName;
|
||||
aiString matName;
|
||||
std::string strId( to_string( mActiveMatGroup ) );
|
||||
std::string strId(to_string(mActiveMatGroup));
|
||||
stdMatName += "id";
|
||||
stdMatName += strId;
|
||||
stdMatName += "_";
|
||||
if ( nullptr != name ) {
|
||||
stdMatName += std::string( name );
|
||||
if (nullptr != name) {
|
||||
stdMatName += std::string(name);
|
||||
} else {
|
||||
stdMatName += "basemat";
|
||||
}
|
||||
matName.Set( stdMatName );
|
||||
matName.Set(stdMatName);
|
||||
|
||||
mat = new aiMaterial;
|
||||
mat->AddProperty( &matName, AI_MATKEY_NAME );
|
||||
mat->AddProperty(&matName, AI_MATKEY_NAME);
|
||||
|
||||
assignDiffuseColor( mat );
|
||||
assignDiffuseColor(mat);
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private:
|
||||
bool ReadToStartElement(const std::string& startTag) {
|
||||
while(xmlReader->read()) {
|
||||
const std::string &nodeName( xmlReader->getNodeName() );
|
||||
bool ReadToStartElement(const std::string &startTag) {
|
||||
while (xmlReader->read()) {
|
||||
const std::string &nodeName(xmlReader->getNodeName());
|
||||
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) {
|
||||
return true;
|
||||
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) {
|
||||
|
@ -390,9 +388,9 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ReadToEndElement(const std::string& closeTag) {
|
||||
while(xmlReader->read()) {
|
||||
const std::string &nodeName( xmlReader->getNodeName() );
|
||||
bool ReadToEndElement(const std::string &closeTag) {
|
||||
while (xmlReader->read()) {
|
||||
const std::string &nodeName(xmlReader->getNodeName());
|
||||
if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
|
||||
return true;
|
||||
} else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) {
|
||||
|
@ -410,11 +408,11 @@ private:
|
|||
std::string value;
|
||||
};
|
||||
std::vector<MetaEntry> mMetaData;
|
||||
std::vector<aiMesh*> mMeshes;
|
||||
std::vector<aiMesh *> mMeshes;
|
||||
MatArray mMatArray;
|
||||
unsigned int mActiveMatGroup;
|
||||
MatId2MatArray mMatId2MatArray;
|
||||
XmlReader* xmlReader;
|
||||
XmlReader *xmlReader;
|
||||
};
|
||||
|
||||
} //namespace D3MF
|
||||
|
@ -432,8 +430,8 @@ static const aiImporterDesc desc = {
|
|||
"3mf"
|
||||
};
|
||||
|
||||
D3MFImporter::D3MFImporter()
|
||||
: BaseImporter() {
|
||||
D3MFImporter::D3MFImporter() :
|
||||
BaseImporter() {
|
||||
// empty
|
||||
}
|
||||
|
||||
|
@ -442,17 +440,17 @@ D3MFImporter::~D3MFImporter() {
|
|||
}
|
||||
|
||||
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
|
||||
const std::string extension( GetExtension( filename ) );
|
||||
if(extension == desc.mFileExtensions ) {
|
||||
const std::string extension(GetExtension(filename));
|
||||
if (extension == desc.mFileExtensions) {
|
||||
return true;
|
||||
} else if ( !extension.length() || checkSig ) {
|
||||
if ( nullptr == pIOHandler ) {
|
||||
} else if (!extension.length() || checkSig) {
|
||||
if (nullptr == pIOHandler) {
|
||||
return false;
|
||||
}
|
||||
if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
|
||||
if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
|
||||
return false;
|
||||
}
|
||||
D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );
|
||||
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
|
||||
return opcPackage.validate();
|
||||
}
|
||||
|
||||
|
@ -467,7 +465,7 @@ const aiImporterDesc *D3MFImporter::GetInfo() const {
|
|||
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);
|
||||
|
||||
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());
|
||||
Object &obj = objects.back();
|
||||
|
||||
aiLight *light = NULL;
|
||||
aiLight *light = nullptr;
|
||||
if (!ASSIMP_strincmp(buffer, "light", 5)) {
|
||||
// This is a light source. Add it to the list
|
||||
mLights->push_back(light = new aiLight());
|
||||
|
@ -535,7 +535,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
|
|||
|
||||
// allocate UV coordinates, but only if the texture name for the
|
||||
// surface is not empty
|
||||
aiVector3D *uv = NULL;
|
||||
aiVector3D *uv = nullptr;
|
||||
if (object.texture.length()) {
|
||||
uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
|
||||
mesh->mNumUVComponents[0] = 2;
|
||||
|
@ -696,7 +696,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
|
|||
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
|
||||
|
||||
// Check whether we can read from the file
|
||||
if ( file.get() == nullptr ) {
|
||||
if (file.get() == nullptr) {
|
||||
throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
|
||||
}
|
||||
|
|
@ -56,27 +56,20 @@ struct aiMesh;
|
|||
struct aiMaterial;
|
||||
struct aiLight;
|
||||
|
||||
|
||||
namespace Assimp {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** AC3D (*.ac) importer class
|
||||
*/
|
||||
class AC3DImporter : public BaseImporter
|
||||
{
|
||||
class AC3DImporter : public BaseImporter {
|
||||
public:
|
||||
AC3DImporter();
|
||||
~AC3DImporter();
|
||||
|
||||
// Represents an AC3D material
|
||||
struct Material
|
||||
{
|
||||
Material()
|
||||
: rgb (0.6f,0.6f,0.6f)
|
||||
, spec (1.f,1.f,1.f)
|
||||
, shin (0.f)
|
||||
, trans (0.f)
|
||||
{}
|
||||
struct Material {
|
||||
Material() :
|
||||
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
|
||||
aiColor3D rgb;
|
||||
|
@ -101,41 +94,23 @@ public:
|
|||
};
|
||||
|
||||
// Represents an AC3D surface
|
||||
struct Surface
|
||||
{
|
||||
Surface()
|
||||
: mat (0)
|
||||
, flags (0)
|
||||
{}
|
||||
struct Surface {
|
||||
Surface() :
|
||||
mat(0), flags(0) {}
|
||||
|
||||
unsigned int mat,flags;
|
||||
unsigned int mat, flags;
|
||||
|
||||
typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
|
||||
std::vector< SurfaceEntry > entries;
|
||||
typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
|
||||
std::vector<SurfaceEntry> entries;
|
||||
};
|
||||
|
||||
// Represents an AC3D object
|
||||
struct 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()
|
||||
{}
|
||||
struct 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 description
|
||||
enum Type
|
||||
{
|
||||
enum Type {
|
||||
World = 0x0,
|
||||
Poly = 0x1,
|
||||
Group = 0x2,
|
||||
|
@ -177,37 +152,33 @@ public:
|
|||
float crease;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns whether the class can handle the format of the given file.
|
||||
* See BaseImporter::CanRead() for details.
|
||||
*/
|
||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
||||
bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
|
||||
bool checkSig) const;
|
||||
|
||||
protected:
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Return importer meta information.
|
||||
* See #BaseImporter::GetInfo for the details */
|
||||
const aiImporterDesc* GetInfo () const;
|
||||
const aiImporterDesc *GetInfo() const;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Imports the given file into the given scene structure.
|
||||
* 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);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Called prior to ReadFile().
|
||||
* The function is a request to the importer to update its configuration
|
||||
* basing on the Importer's configuration property list.*/
|
||||
void SetupProperties(const Importer* pImp);
|
||||
void SetupProperties(const Importer *pImp);
|
||||
|
||||
private:
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Get the next line from the file.
|
||||
* @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
|
||||
* encountered.
|
||||
* @objects List of output objects*/
|
||||
void LoadObjectSection(std::vector<Object>& objects);
|
||||
void LoadObjectSection(std::vector<Object> &objects);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Convert all objects into meshes and nodes.
|
||||
|
@ -227,24 +198,24 @@ private:
|
|||
* @param outMaterials List of output materials
|
||||
* @param materials Material list
|
||||
* @param Scenegraph node for the object */
|
||||
aiNode* ConvertObjectSection(Object& object,
|
||||
std::vector<aiMesh*>& meshes,
|
||||
std::vector<aiMaterial*>& outMaterials,
|
||||
const std::vector<Material>& materials,
|
||||
aiNode* parent = NULL);
|
||||
aiNode *ConvertObjectSection(Object &object,
|
||||
std::vector<aiMesh *> &meshes,
|
||||
std::vector<aiMaterial *> &outMaterials,
|
||||
const std::vector<Material> &materials,
|
||||
aiNode *parent = nullptr);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Convert a material
|
||||
* @param object Current object
|
||||
* @param matSrc Source material description
|
||||
* @param matDest Destination material to be filled */
|
||||
void ConvertMaterial(const Object& object,
|
||||
const Material& matSrc,
|
||||
aiMaterial& matDest);
|
||||
void ConvertMaterial(const Object &object,
|
||||
const Material &matSrc,
|
||||
aiMaterial &matDest);
|
||||
|
||||
private:
|
||||
// points to the next data line
|
||||
const char* buffer;
|
||||
const char *buffer;
|
||||
|
||||
// Configuration option: if enabled, up to two meshes
|
||||
// are generated per material: those faces who have
|
||||
|
@ -261,7 +232,7 @@ private:
|
|||
unsigned int mNumMeshes;
|
||||
|
||||
// current list of light sources
|
||||
std::vector<aiLight*>* mLights;
|
||||
std::vector<aiLight *> *mLights;
|
||||
|
||||
// name counters
|
||||
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>
|
||||
|
||||
// ASE is quite similar to 3ds. We can reuse some structures
|
||||
#include "3DS/3DSLoader.h"
|
||||
#include "AssetLib/3DS/3DSLoader.h"
|
||||
|
||||
namespace Assimp {
|
||||
namespace ASE {
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
|||
|
||||
Copyright (c) 2006-2020, assimp team
|
||||
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
|||
|
||||
Copyright (c) 2006-2020, assimp team
|
||||
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
|
@ -54,16 +53,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <assimp/IOStream.hpp>
|
||||
|
||||
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
||||
# include <zlib.h>
|
||||
#include <zlib.h>
|
||||
#else
|
||||
# include "../contrib/zlib/zlib.h"
|
||||
#include "../contrib/zlib/zlib.h"
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4706)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4706)
|
||||
#endif // _WIN32
|
||||
|
||||
namespace Assimp {
|
||||
|
@ -269,7 +268,7 @@ private:
|
|||
|
||||
public:
|
||||
AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
|
||||
buffer(NULL),
|
||||
buffer(nullptr),
|
||||
magic(magic),
|
||||
container(container),
|
||||
cur_size(0),
|
||||
|
@ -827,7 +826,7 @@ void DumpSceneToAssbin(
|
|||
fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
# pragma warning(pop)
|
||||
#pragma warning(pop)
|
||||
#endif // _WIN32
|
||||
|
||||
} // end of namespace Assimp
|
|
@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
|
|||
|
||||
Copyright (c) 2006-2020, assimp team
|
||||
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
|
@ -54,10 +53,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace Assimp {
|
||||
|
||||
void ASSIMP_API DumpSceneToAssbin(
|
||||
const char* pFile,
|
||||
const char* cmd,
|
||||
IOSystem* pIOSystem,
|
||||
const aiScene* pScene,
|
||||
const char *pFile,
|
||||
const char *cmd,
|
||||
IOSystem *pIOSystem,
|
||||
const aiScene *pScene,
|
||||
bool shortened,
|
||||
bool compressed);
|
||||
|
|
@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
|
||||
|
||||
// internal headers
|
||||
#include "Assbin/AssbinLoader.h"
|
||||
#include "AssetLib/Assbin/AssbinLoader.h"
|
||||
#include "Common/assbin_chunks.h"
|
||||
#include <assimp/MemoryIOWrapper.h>
|
||||
#include <assimp/anim.h>
|
|
@ -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;
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
namespace Assimp {
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
/** 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
|
||||
* 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 */
|
||||
enum ChannelType
|
||||
{
|
||||
enum ChannelType {
|
||||
Channel_PositionX,
|
||||
Channel_PositionY,
|
||||
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 */
|
||||
struct Node
|
||||
{
|
||||
const aiNode* mNode;
|
||||
struct Node {
|
||||
const aiNode *mNode;
|
||||
std::vector<ChannelType> mChannels;
|
||||
std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
|
||||
|
||||
Node()
|
||||
: mNode(nullptr)
|
||||
{ }
|
||||
Node() :
|
||||
mNode(nullptr) {}
|
||||
|
||||
explicit Node( const aiNode* pNode) : mNode( pNode) { }
|
||||
explicit Node(const aiNode *pNode) :
|
||||
mNode(pNode) {}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
BVHLoader();
|
||||
~BVHLoader();
|
||||
|
||||
public:
|
||||
/** Returns whether the class can handle the format of the given file.
|
||||
* 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);
|
||||
const aiImporterDesc* GetInfo () const;
|
||||
void SetupProperties(const Importer *pImp);
|
||||
const aiImporterDesc *GetInfo() const;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/** Imports the given file into the given scene structure.
|
||||
* 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:
|
||||
/** Reads the file */
|
||||
void ReadStructure( aiScene* pScene);
|
||||
void ReadStructure(aiScene *pScene);
|
||||
|
||||
/** Reads the hierarchy */
|
||||
void ReadHierarchy( aiScene* pScene);
|
||||
void ReadHierarchy(aiScene *pScene);
|
||||
|
||||
/** 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. */
|
||||
aiNode* ReadEndSite( const std::string& pParentName);
|
||||
aiNode *ReadEndSite(const std::string &pParentName);
|
||||
|
||||
/** Reads a node offset for the given node */
|
||||
void ReadNodeOffset( aiNode* pNode);
|
||||
void ReadNodeOffset(aiNode *pNode);
|
||||
|
||||
/** Reads the animation channels into the given node */
|
||||
void ReadNodeChannels( BVHLoader::Node& pNode);
|
||||
void ReadNodeChannels(BVHLoader::Node &pNode);
|
||||
|
||||
/** Reads the motion data */
|
||||
void ReadMotion( aiScene* pScene);
|
||||
void ReadMotion(aiScene *pScene);
|
||||
|
||||
/** Retrieves the next token */
|
||||
std::string GetNextToken();
|
||||
|
@ -141,10 +134,10 @@ protected:
|
|||
float GetNextTokenAsFloat();
|
||||
|
||||
/** 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 */
|
||||
void CreateAnimation( aiScene* pScene);
|
||||
void CreateAnimation(aiScene *pScene);
|
||||
|
||||
protected:
|
||||
/** 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
|
||||
|
||||
#include "BlenderBMesh.h"
|
||||
#include "BlenderDNA.h"
|
||||
#include "BlenderScene.h"
|
||||
#include "BlenderBMesh.h"
|
||||
#include "BlenderTessellator.h"
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix()
|
||||
{
|
||||
namespace Assimp {
|
||||
template <>
|
||||
const char *LogFunctions<BlenderBMeshConverter>::Prefix() {
|
||||
static auto prefix = "BLEND_BMESH: ";
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
} // namespace Assimp
|
||||
|
||||
using namespace Assimp;
|
||||
using namespace Assimp::Blender;
|
||||
using namespace Assimp::Formatter;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
|
||||
BMesh( mesh ),
|
||||
triMesh( NULL )
|
||||
{
|
||||
BlenderBMeshConverter::BlenderBMeshConverter(const Mesh *mesh) :
|
||||
BMesh(mesh),
|
||||
triMesh(nullptr) {
|
||||
// empty
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BlenderBMeshConverter::~BlenderBMeshConverter( )
|
||||
{
|
||||
DestroyTriMesh( );
|
||||
BlenderBMeshConverter::~BlenderBMeshConverter() {
|
||||
DestroyTriMesh();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool BlenderBMeshConverter::ContainsBMesh( ) const
|
||||
{
|
||||
bool BlenderBMeshConverter::ContainsBMesh() const {
|
||||
// TODO - Should probably do some additional verification here
|
||||
return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
|
||||
{
|
||||
AssertValidMesh( );
|
||||
AssertValidSizes( );
|
||||
PrepareTriMesh( );
|
||||
const Mesh *BlenderBMeshConverter::TriangulateBMesh() {
|
||||
AssertValidMesh();
|
||||
AssertValidSizes();
|
||||
PrepareTriMesh();
|
||||
|
||||
for ( int i = 0; i < BMesh->totpoly; ++i )
|
||||
{
|
||||
const MPoly& poly = BMesh->mpoly[ i ];
|
||||
ConvertPolyToFaces( poly );
|
||||
for (int i = 0; i < BMesh->totpoly; ++i) {
|
||||
const MPoly &poly = BMesh->mpoly[i];
|
||||
ConvertPolyToFaces(poly);
|
||||
}
|
||||
|
||||
return triMesh;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BlenderBMeshConverter::AssertValidMesh( )
|
||||
{
|
||||
if ( !ContainsBMesh( ) )
|
||||
{
|
||||
ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
|
||||
void BlenderBMeshConverter::AssertValidMesh() {
|
||||
if (!ContainsBMesh()) {
|
||||
ThrowException("BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BlenderBMeshConverter::AssertValidSizes( )
|
||||
{
|
||||
if ( BMesh->totpoly != static_cast<int>( BMesh->mpoly.size( ) ) )
|
||||
{
|
||||
ThrowException( "BMesh poly array has incorrect size" );
|
||||
void BlenderBMeshConverter::AssertValidSizes() {
|
||||
if (BMesh->totpoly != static_cast<int>(BMesh->mpoly.size())) {
|
||||
ThrowException("BMesh poly array has incorrect size");
|
||||
}
|
||||
if ( BMesh->totloop != static_cast<int>( BMesh->mloop.size( ) ) )
|
||||
{
|
||||
ThrowException( "BMesh loop array has incorrect size" );
|
||||
if (BMesh->totloop != static_cast<int>(BMesh->mloop.size())) {
|
||||
ThrowException("BMesh loop array has incorrect size");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BlenderBMeshConverter::PrepareTriMesh( )
|
||||
{
|
||||
if ( triMesh )
|
||||
{
|
||||
DestroyTriMesh( );
|
||||
void BlenderBMeshConverter::PrepareTriMesh() {
|
||||
if (triMesh) {
|
||||
DestroyTriMesh();
|
||||
}
|
||||
|
||||
triMesh = new Mesh( *BMesh );
|
||||
triMesh = new Mesh(*BMesh);
|
||||
triMesh->totface = 0;
|
||||
triMesh->mface.clear( );
|
||||
triMesh->mface.clear();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BlenderBMeshConverter::DestroyTriMesh( )
|
||||
{
|
||||
void BlenderBMeshConverter::DestroyTriMesh() {
|
||||
delete triMesh;
|
||||
triMesh = NULL;
|
||||
triMesh = nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
|
||||
{
|
||||
const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
|
||||
void BlenderBMeshConverter::ConvertPolyToFaces(const MPoly &poly) {
|
||||
const MLoop *polyLoop = &BMesh->mloop[poly.loopstart];
|
||||
|
||||
if ( poly.totloop == 3 || poly.totloop == 4 )
|
||||
{
|
||||
AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
|
||||
if (poly.totloop == 3 || poly.totloop == 4) {
|
||||
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.
|
||||
if ( BMesh->mloopuv.size() )
|
||||
{
|
||||
if ( (poly.loopstart + poly.totloop ) > static_cast<int>( BMesh->mloopuv.size() ) )
|
||||
{
|
||||
ThrowException( "BMesh uv loop array has incorrect size" );
|
||||
if (BMesh->mloopuv.size()) {
|
||||
if ((poly.loopstart + poly.totloop) > static_cast<int>(BMesh->mloopuv.size())) {
|
||||
ThrowException("BMesh uv loop array has incorrect size");
|
||||
}
|
||||
const MLoopUV* loopUV = &BMesh->mloopuv[ poly.loopstart ];
|
||||
AddTFace( loopUV[ 0 ].uv, loopUV[ 1 ].uv, loopUV[ 2 ].uv, poly.totloop == 4 ? loopUV[ 3 ].uv : 0 );
|
||||
const MLoopUV *loopUV = &BMesh->mloopuv[poly.loopstart];
|
||||
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
|
||||
BlenderTessellatorGL tessGL( *this );
|
||||
tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
|
||||
BlenderTessellatorGL tessGL(*this);
|
||||
tessGL.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
|
||||
#elif ASSIMP_BLEND_WITH_POLY_2_TRI
|
||||
BlenderTessellatorP2T tessP2T( *this );
|
||||
tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
|
||||
BlenderTessellatorP2T tessP2T(*this);
|
||||
tessP2T.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
|
||||
#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;
|
||||
face.v1 = v1;
|
||||
face.v2 = v2;
|
||||
|
@ -183,24 +163,22 @@ void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
|
|||
face.flag = 0;
|
||||
// TODO - Work out how materials work
|
||||
face.mat_nr = 0;
|
||||
triMesh->mface.push_back( face );
|
||||
triMesh->totface = static_cast<int>(triMesh->mface.size( ));
|
||||
triMesh->mface.push_back(face);
|
||||
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;
|
||||
memcpy( &mtface.uv[ 0 ], uv1, sizeof(float) * 2 );
|
||||
memcpy( &mtface.uv[ 1 ], uv2, sizeof(float) * 2 );
|
||||
memcpy( &mtface.uv[ 2 ], uv3, sizeof(float) * 2 );
|
||||
memcpy(&mtface.uv[0], uv1, sizeof(float) * 2);
|
||||
memcpy(&mtface.uv[1], uv2, sizeof(float) * 2);
|
||||
memcpy(&mtface.uv[2], uv3, sizeof(float) * 2);
|
||||
|
||||
if ( uv4 )
|
||||
{
|
||||
memcpy( &mtface.uv[ 3 ], uv4, sizeof(float) * 2 );
|
||||
if (uv4) {
|
||||
memcpy(&mtface.uv[3], uv4, sizeof(float) * 2);
|
||||
}
|
||||
|
||||
triMesh->mtface.push_back( mtface );
|
||||
triMesh->mtface.push_back(mtface);
|
||||
}
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||
#include "BlenderDNA.h"
|
||||
#include <assimp/StreamReader.h>
|
||||
#include <assimp/fast_atof.h>
|
||||
#include <assimp/TinyFormatter.h>
|
||||
#include <assimp/fast_atof.h>
|
||||
|
||||
using namespace Assimp;
|
||||
using namespace Assimp::Blender;
|
||||
using namespace Assimp::Formatter;
|
||||
|
||||
static bool match4(StreamReaderAny& stream, const char* string) {
|
||||
ai_assert( nullptr != string );
|
||||
static bool match4(StreamReaderAny &stream, const char *string) {
|
||||
ai_assert(nullptr != string);
|
||||
char tmp[4];
|
||||
tmp[ 0 ] = ( stream ).GetI1();
|
||||
tmp[ 1 ] = ( stream ).GetI1();
|
||||
tmp[ 2 ] = ( stream ).GetI1();
|
||||
tmp[ 3 ] = ( stream ).GetI1();
|
||||
return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
|
||||
tmp[0] = (stream).GetI1();
|
||||
tmp[1] = (stream).GetI1();
|
||||
tmp[2] = (stream).GetI1();
|
||||
tmp[3] = (stream).GetI1();
|
||||
return (tmp[0] == string[0] && tmp[1] == string[1] && tmp[2] == string[2] && tmp[3] == string[3]);
|
||||
}
|
||||
|
||||
struct Type {
|
||||
|
@ -72,74 +71,75 @@ struct Type {
|
|||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DNAParser::Parse ()
|
||||
{
|
||||
StreamReaderAny& stream = *db.reader.get();
|
||||
DNA& dna = db.dna;
|
||||
void DNAParser::Parse() {
|
||||
StreamReaderAny &stream = *db.reader.get();
|
||||
DNA &dna = db.dna;
|
||||
|
||||
if(!match4(stream,"SDNA")) {
|
||||
if (!match4(stream, "SDNA")) {
|
||||
throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
|
||||
}
|
||||
|
||||
// name dictionary
|
||||
if(!match4(stream,"NAME")) {
|
||||
if (!match4(stream, "NAME")) {
|
||||
throw DeadlyImportError("BlenderDNA: Expected NAME field");
|
||||
}
|
||||
|
||||
std::vector<std::string> names (stream.GetI4());
|
||||
for(std::string& s : names) {
|
||||
std::vector<std::string> names(stream.GetI4());
|
||||
for (std::string &s : names) {
|
||||
while (char c = stream.GetI1()) {
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
|
||||
// type dictionary
|
||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
||||
if(!match4(stream,"TYPE")) {
|
||||
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||
;
|
||||
if (!match4(stream, "TYPE")) {
|
||||
throw DeadlyImportError("BlenderDNA: Expected TYPE field");
|
||||
}
|
||||
|
||||
std::vector<Type> types (stream.GetI4());
|
||||
for(Type& s : types) {
|
||||
std::vector<Type> types(stream.GetI4());
|
||||
for (Type &s : types) {
|
||||
while (char c = stream.GetI1()) {
|
||||
s.name += c;
|
||||
}
|
||||
}
|
||||
|
||||
// type length dictionary
|
||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
||||
if(!match4(stream,"TLEN")) {
|
||||
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||
;
|
||||
if (!match4(stream, "TLEN")) {
|
||||
throw DeadlyImportError("BlenderDNA: Expected TLEN field");
|
||||
}
|
||||
|
||||
for(Type& s : types) {
|
||||
for (Type &s : types) {
|
||||
s.size = stream.GetI2();
|
||||
}
|
||||
|
||||
// structures dictionary
|
||||
for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
|
||||
if(!match4(stream,"STRC")) {
|
||||
for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
|
||||
;
|
||||
if (!match4(stream, "STRC")) {
|
||||
throw DeadlyImportError("BlenderDNA: Expected STRC field");
|
||||
}
|
||||
|
||||
size_t end = stream.GetI4(), fields = 0;
|
||||
|
||||
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();
|
||||
if (n >= types.size()) {
|
||||
throw DeadlyImportError((format(),
|
||||
"BlenderDNA: Invalid type index in structure name" ,n,
|
||||
" (there are only ", types.size(), " entries)"
|
||||
));
|
||||
"BlenderDNA: Invalid type index in structure name", n,
|
||||
" (there are only ", types.size(), " entries)"));
|
||||
}
|
||||
|
||||
// maintain separate indexes
|
||||
dna.indices[types[n].name] = dna.structures.size();
|
||||
|
||||
dna.structures.push_back(Structure());
|
||||
Structure& s = dna.structures.back();
|
||||
Structure &s = dna.structures.back();
|
||||
s.name = types[n].name;
|
||||
//s.index = dna.structures.size()-1;
|
||||
|
||||
|
@ -153,11 +153,10 @@ void DNAParser::Parse ()
|
|||
if (j >= types.size()) {
|
||||
throw DeadlyImportError((format(),
|
||||
"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());
|
||||
Field& f = s.fields.back();
|
||||
Field &f = s.fields.back();
|
||||
f.offset = offset;
|
||||
|
||||
f.type = types[j].name;
|
||||
|
@ -167,8 +166,7 @@ void DNAParser::Parse ()
|
|||
if (j >= names.size()) {
|
||||
throw DeadlyImportError((format(),
|
||||
"BlenderDNA: Invalid name index in structure field ", j,
|
||||
" (there are only ", names.size(), " entries)"
|
||||
));
|
||||
" (there are only ", names.size(), " entries)"));
|
||||
}
|
||||
|
||||
f.name = names[j];
|
||||
|
@ -192,25 +190,24 @@ void DNAParser::Parse ()
|
|||
if (rb == std::string::npos) {
|
||||
throw DeadlyImportError((format(),
|
||||
"BlenderDNA: Encountered invalid array declaration ",
|
||||
f.name
|
||||
));
|
||||
f.name));
|
||||
}
|
||||
|
||||
f.flags |= FieldFlag_Array;
|
||||
DNA::ExtractArraySize(f.name,f.array_sizes);
|
||||
f.name = f.name.substr(0,rb);
|
||||
DNA::ExtractArraySize(f.name, f.array_sizes);
|
||||
f.name = f.name.substr(0, rb);
|
||||
|
||||
f.size *= f.array_sizes[0] * f.array_sizes[1];
|
||||
}
|
||||
|
||||
// maintain separate indexes
|
||||
s.indices[f.name] = s.fields.size()-1;
|
||||
s.indices[f.name] = s.fields.size() - 1;
|
||||
offset += f.size;
|
||||
}
|
||||
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
|
||||
dna.DumpToFile();
|
||||
|
@ -220,13 +217,11 @@ void DNAParser::Parse ()
|
|||
dna.RegisterConverters();
|
||||
}
|
||||
|
||||
|
||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||
|
||||
#include <fstream>
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DNA :: DumpToFile()
|
||||
{
|
||||
void DNA ::DumpToFile() {
|
||||
// we don't bother using the VFS here for this is only for debugging.
|
||||
// (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");
|
||||
return;
|
||||
}
|
||||
f << "Field format: type name offset size" << "\n";
|
||||
f << "Structure format: name size" << "\n";
|
||||
f << "Field format: type name offset size"
|
||||
<< "\n";
|
||||
f << "Structure format: name size"
|
||||
<< "\n";
|
||||
|
||||
for(const Structure& s : structures) {
|
||||
for (const Structure &s : structures) {
|
||||
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 << "\n";
|
||||
|
@ -252,11 +249,9 @@ void DNA :: DumpToFile()
|
|||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void DNA :: ExtractArraySize(
|
||||
const std::string& out,
|
||||
size_t array_sizes[2]
|
||||
)
|
||||
{
|
||||
/*static*/ void DNA ::ExtractArraySize(
|
||||
const std::string &out,
|
||||
size_t array_sizes[2]) {
|
||||
array_sizes[0] = array_sizes[1] = 1;
|
||||
std::string::size_type pos = out.find('[');
|
||||
if (pos++ == std::string::npos) {
|
||||
|
@ -264,7 +259,7 @@ void DNA :: DumpToFile()
|
|||
}
|
||||
array_sizes[0] = strtoul10(&out[pos]);
|
||||
|
||||
pos = out.find('[',pos);
|
||||
pos = out.find('[', pos);
|
||||
if (pos++ == std::string::npos) {
|
||||
return;
|
||||
}
|
||||
|
@ -272,36 +267,32 @@ void DNA :: DumpToFile()
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
|
||||
const Structure& structure,
|
||||
const FileDatabase& db
|
||||
) const
|
||||
{
|
||||
std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
|
||||
std::shared_ptr<ElemBase> DNA ::ConvertBlobToStructure(
|
||||
const Structure &structure,
|
||||
const FileDatabase &db) const {
|
||||
std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
|
||||
if (it == converters.end()) {
|
||||
return std::shared_ptr< ElemBase >();
|
||||
return std::shared_ptr<ElemBase>();
|
||||
}
|
||||
|
||||
std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
|
||||
(structure.*((*it).second.second))(ret,db);
|
||||
std::shared_ptr<ElemBase> ret = (structure.*((*it).second.first))();
|
||||
(structure.*((*it).second.second))(ret, db);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DNA::FactoryPair DNA :: GetBlobToStructureConverter(
|
||||
const Structure& structure,
|
||||
const FileDatabase& /*db*/
|
||||
) const
|
||||
{
|
||||
DNA::FactoryPair DNA ::GetBlobToStructureConverter(
|
||||
const Structure &structure,
|
||||
const FileDatabase & /*db*/
|
||||
) const {
|
||||
std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
|
||||
return it == converters.end() ? FactoryPair() : (*it).second;
|
||||
}
|
||||
|
||||
// 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
|
||||
// Structure::Convert<target_type> to be called on these
|
||||
// empty structures. These converters are special
|
||||
|
@ -311,30 +302,27 @@ void DNA :: AddPrimitiveStructures()
|
|||
// in question.
|
||||
|
||||
indices["int"] = structures.size();
|
||||
structures.push_back( Structure() );
|
||||
structures.push_back(Structure());
|
||||
structures.back().name = "int";
|
||||
structures.back().size = 4;
|
||||
|
||||
indices["short"] = structures.size();
|
||||
structures.push_back( Structure() );
|
||||
structures.push_back(Structure());
|
||||
structures.back().name = "short";
|
||||
structures.back().size = 2;
|
||||
|
||||
|
||||
indices["char"] = structures.size();
|
||||
structures.push_back( Structure() );
|
||||
structures.push_back(Structure());
|
||||
structures.back().name = "char";
|
||||
structures.back().size = 1;
|
||||
|
||||
|
||||
indices["float"] = structures.size();
|
||||
structures.push_back( Structure() );
|
||||
structures.push_back(Structure());
|
||||
structures.back().name = "float";
|
||||
structures.back().size = 4;
|
||||
|
||||
|
||||
indices["double"] = structures.size();
|
||||
structures.push_back( Structure() );
|
||||
structures.push_back(Structure());
|
||||
structures.back().name = "double";
|
||||
structures.back().size = 8;
|
||||
|
||||
|
@ -342,8 +330,7 @@ void DNA :: AddPrimitiveStructures()
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SectionParser :: Next()
|
||||
{
|
||||
void SectionParser ::Next() {
|
||||
stream.SetCurrentPos(current.start + current.size);
|
||||
|
||||
const char tmp[] = {
|
||||
|
@ -352,7 +339,7 @@ void SectionParser :: Next()
|
|||
(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.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
|
||||
|
@ -370,6 +357,4 @@ void SectionParser :: Next()
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -49,22 +49,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <assimp/BaseImporter.h>
|
||||
#include <assimp/StreamReader.h>
|
||||
#include <assimp/DefaultLogger.hpp>
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <assimp/DefaultLogger.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// enable verbose log output. really verbose, so be careful.
|
||||
#ifdef ASSIMP_BUILD_DEBUG
|
||||
# define ASSIMP_BUILD_BLENDER_DEBUG
|
||||
#define ASSIMP_BUILD_BLENDER_DEBUG
|
||||
#endif
|
||||
|
||||
// #define ASSIMP_BUILD_BLENDER_NO_STATS
|
||||
|
||||
namespace Assimp {
|
||||
|
||||
template <bool,bool> class StreamReader;
|
||||
typedef StreamReader<true,true> StreamReaderAny;
|
||||
template <bool, bool>
|
||||
class StreamReader;
|
||||
typedef StreamReader<true, true> StreamReaderAny;
|
||||
|
||||
namespace Blender {
|
||||
|
||||
|
@ -82,8 +83,8 @@ class ObjectCache;
|
|||
* ancestry. */
|
||||
// -------------------------------------------------------------------------------
|
||||
struct Error : DeadlyImportError {
|
||||
Error (const std::string& s)
|
||||
: DeadlyImportError(s) {
|
||||
Error(const std::string &s) :
|
||||
DeadlyImportError(s) {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
@ -93,9 +94,8 @@ struct Error : DeadlyImportError {
|
|||
* descendents. It serves as base class for all data structure fields. */
|
||||
// -------------------------------------------------------------------------------
|
||||
struct ElemBase {
|
||||
ElemBase()
|
||||
: dna_type(nullptr)
|
||||
{
|
||||
ElemBase() :
|
||||
dna_type(nullptr) {
|
||||
// empty
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ struct ElemBase {
|
|||
* data type is not static, i.e. a std::shared_ptr<ElemBase>
|
||||
* in the scene description would have its type resolved
|
||||
* 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.*/
|
||||
// -------------------------------------------------------------------------------
|
||||
struct Pointer {
|
||||
Pointer()
|
||||
: val() {
|
||||
Pointer() :
|
||||
val() {
|
||||
// empty
|
||||
}
|
||||
uint64_t val;
|
||||
|
@ -131,8 +131,8 @@ struct Pointer {
|
|||
/** Represents a generic offset within a BLEND file */
|
||||
// -------------------------------------------------------------------------------
|
||||
struct FileOffset {
|
||||
FileOffset()
|
||||
: val() {
|
||||
FileOffset() :
|
||||
val() {
|
||||
// empty
|
||||
}
|
||||
uint64_t val;
|
||||
|
@ -154,7 +154,7 @@ public:
|
|||
resize(0);
|
||||
}
|
||||
|
||||
operator bool () const {
|
||||
operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
};
|
||||
|
@ -200,7 +200,7 @@ enum ErrorPolicy {
|
|||
};
|
||||
|
||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||
# define ErrorPolicy_Igno ErrorPolicy_Warn
|
||||
#define ErrorPolicy_Igno ErrorPolicy_Warn
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
|
@ -212,47 +212,42 @@ enum ErrorPolicy {
|
|||
* meaningful contents. */
|
||||
// -------------------------------------------------------------------------------
|
||||
class Structure {
|
||||
template <template <typename> class> friend class ObjectCache;
|
||||
template <template <typename> class>
|
||||
friend class ObjectCache;
|
||||
|
||||
public:
|
||||
Structure()
|
||||
: cache_idx(static_cast<size_t>(-1) ){
|
||||
Structure() :
|
||||
cache_idx(static_cast<size_t>(-1)) {
|
||||
// empty
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// publicly accessible members
|
||||
std::string name;
|
||||
vector< Field > fields;
|
||||
vector<Field> fields;
|
||||
std::map<std::string, size_t> indices;
|
||||
|
||||
size_t size;
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** 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. */
|
||||
inline const Field& operator [] (const std::string& ss) const;
|
||||
inline const Field* Get (const std::string& ss) const;
|
||||
inline const Field &operator[](const std::string &ss) const;
|
||||
inline const Field *Get(const std::string &ss) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** 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
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
inline bool operator!= (const Structure& other) const {
|
||||
inline bool operator!=(const Structure &other) const {
|
||||
return name != other.name;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Try to read an instance of the structure from the stream
|
||||
* and attempt to convert to `T`. This is done by
|
||||
|
@ -260,38 +255,38 @@ public:
|
|||
* a compiler complain is the result.
|
||||
* @param dest Destination value to be written
|
||||
* @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
|
||||
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
|
||||
template <typename T> std::shared_ptr<ElemBase> Allocate() const;
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<ElemBase> Allocate() const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// field parsing for 1d arrays
|
||||
template <int error_policy, typename T, size_t M>
|
||||
void ReadFieldArray(T (& out)[M], const char* name,
|
||||
const FileDatabase& db) const;
|
||||
void ReadFieldArray(T (&out)[M], const char *name,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// field parsing for 2d arrays
|
||||
template <int error_policy, typename T, size_t M, size_t N>
|
||||
void ReadFieldArray2(T (& out)[M][N], const char* name,
|
||||
const FileDatabase& db) const;
|
||||
void ReadFieldArray2(T (&out)[M][N], const char *name,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// field parsing for pointer or dynamic array types
|
||||
// (std::shared_ptr)
|
||||
// The return value indicates whether the data was already cached.
|
||||
template <int error_policy, template <typename> class TOUT, typename T>
|
||||
bool ReadFieldPtr(TOUT<T>& out, const char* name,
|
||||
const FileDatabase& db,
|
||||
bool ReadFieldPtr(TOUT<T> &out, const char *name,
|
||||
const FileDatabase &db,
|
||||
bool non_recursive = false) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
@ -299,15 +294,15 @@ public:
|
|||
// array types (std::shared_ptr[])
|
||||
// The return value indicates whether the data was already cached.
|
||||
template <int error_policy, template <typename> class TOUT, typename T, size_t N>
|
||||
bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
|
||||
const FileDatabase& db) const;
|
||||
bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// field parsing for `normal` values
|
||||
// The return value indicates whether the data was already cached.
|
||||
template <int error_policy, typename T>
|
||||
void ReadField(T& out, const char* name,
|
||||
const FileDatabase& db) const;
|
||||
void ReadField(T &out, const char *name,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
/**
|
||||
|
@ -318,7 +313,7 @@ public:
|
|||
* @return true when read was successful
|
||||
*/
|
||||
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
|
||||
|
@ -329,40 +324,40 @@ public:
|
|||
* @return true when read was successful
|
||||
*/
|
||||
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:
|
||||
|
||||
// --------------------------------------------------------
|
||||
template <template <typename> class TOUT, typename T>
|
||||
bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
|
||||
const FileDatabase& db, const Field& f,
|
||||
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>
|
||||
bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
|
||||
const FileDatabase& db, const Field& f, bool) const;
|
||||
bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval,
|
||||
const FileDatabase &db, const Field &f, bool) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
bool ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
|
||||
const FileDatabase& db, const Field& f, bool) const;
|
||||
bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval,
|
||||
const FileDatabase &db, const Field &f, bool) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
inline const FileBlockHead* LocateFileBlockForAddress(
|
||||
const Pointer & ptrval,
|
||||
const FileDatabase& db) const;
|
||||
inline const FileBlockHead *LocateFileBlockForAddress(
|
||||
const Pointer &ptrval,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
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());
|
||||
s = 1;
|
||||
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);
|
||||
return s ? &out.front() : NULL;
|
||||
}
|
||||
|
@ -372,14 +367,14 @@ private:
|
|||
struct _defaultInitializer {
|
||||
|
||||
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) {
|
||||
out[i] = T();
|
||||
}
|
||||
}
|
||||
|
||||
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 j = 0; j < M; ++j) {
|
||||
out[i][j] = T();
|
||||
|
@ -388,21 +383,21 @@ private:
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void operator ()(T& out, const char* = NULL) {
|
||||
void operator()(T &out, const char * = NULL) {
|
||||
out = T();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
mutable size_t cache_idx;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------
|
||||
template <> struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
|
||||
template <>
|
||||
struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
|
||||
|
||||
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);
|
||||
|
||||
// ... 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>
|
||||
void operator ()(T& /*out*/,const char* = "") {
|
||||
void operator()(T & /*out*/, const char * = "") {
|
||||
// obviously, it is crucial that _DefaultInitializer is used
|
||||
// only from within a catch clause.
|
||||
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,
|
||||
const Pointer & ptrval,
|
||||
const FileDatabase& db,
|
||||
const Field& f,
|
||||
bool
|
||||
) const;
|
||||
|
||||
template <>
|
||||
inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out,
|
||||
const Pointer &ptrval,
|
||||
const FileDatabase &db,
|
||||
const Field &f,
|
||||
bool) const;
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** 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
|
||||
* DNA is altered.*/
|
||||
// -------------------------------------------------------------------------------
|
||||
class DNA
|
||||
{
|
||||
class DNA {
|
||||
public:
|
||||
|
||||
typedef void (Structure::*ConvertProcPtr) (
|
||||
typedef void (Structure::*ConvertProcPtr)(
|
||||
std::shared_ptr<ElemBase> in,
|
||||
const FileDatabase&
|
||||
) const;
|
||||
const FileDatabase &) const;
|
||||
|
||||
typedef std::shared_ptr<ElemBase> (
|
||||
Structure::*AllocProcPtr) () const;
|
||||
Structure::*AllocProcPtr)() const;
|
||||
|
||||
typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
|
||||
typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair;
|
||||
|
||||
public:
|
||||
|
||||
std::map<std::string, FactoryPair > converters;
|
||||
vector<Structure > structures;
|
||||
std::map<std::string, FactoryPair> converters;
|
||||
vector<Structure> structures;
|
||||
std::map<std::string, size_t> indices;
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Access a structure by its canonical name, the pointer version returns NULL on failure
|
||||
* while the reference version raises an error. */
|
||||
inline const Structure& operator [] (const std::string& ss) const;
|
||||
inline const Structure* Get (const std::string& ss) const;
|
||||
inline const Structure &operator[](const std::string &ss) const;
|
||||
inline const Structure *Get(const std::string &ss) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** 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:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Add structure definitions for all the primitive types,
|
||||
* i.e. integer, short, char, float */
|
||||
|
@ -483,7 +472,6 @@ public:
|
|||
* known at compile time (consier Object::data).*/
|
||||
void RegisterConverters();
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Take an input blob from the stream, interpret it according to
|
||||
* a its structure name and convert it to the intermediate
|
||||
|
@ -491,10 +479,9 @@ public:
|
|||
* @param structure Destination structure definition
|
||||
* @param db File database.
|
||||
* @return A null pointer if no appropriate converter is available.*/
|
||||
std::shared_ptr< ElemBase > ConvertBlobToStructure(
|
||||
const Structure& structure,
|
||||
const FileDatabase& db
|
||||
) const;
|
||||
std::shared_ptr<ElemBase> ConvertBlobToStructure(
|
||||
const Structure &structure,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Find a suitable conversion function for a given Structure.
|
||||
|
@ -505,10 +492,8 @@ public:
|
|||
* @param db File database.
|
||||
* @return A null pointer in .first if no appropriate converter is available.*/
|
||||
FactoryPair GetBlobToStructureConverter(
|
||||
const Structure& structure,
|
||||
const FileDatabase& db
|
||||
) const;
|
||||
|
||||
const Structure &structure,
|
||||
const FileDatabase &db) const;
|
||||
|
||||
#ifdef ASSIMP_BUILD_BLENDER_DEBUG
|
||||
// --------------------------------------------------------
|
||||
|
@ -527,25 +512,29 @@ public:
|
|||
* @throw DeadlyImportError if more than 2 dimensions are
|
||||
* encountered. */
|
||||
static void ExtractArraySize(
|
||||
const std::string& out,
|
||||
size_t array_sizes[2]
|
||||
);
|
||||
const std::string &out,
|
||||
size_t array_sizes[2]);
|
||||
};
|
||||
|
||||
// special converters for primitive types
|
||||
template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const;
|
||||
template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const;
|
||||
template <> 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;
|
||||
template <>
|
||||
inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const;
|
||||
template <>
|
||||
inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const;
|
||||
template <>
|
||||
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
|
||||
* elements of a certain SDNA structure (or otherwise unspecified data). */
|
||||
// -------------------------------------------------------------------------------
|
||||
struct FileBlockHead
|
||||
{
|
||||
struct FileBlockHead {
|
||||
// points right after the header of the file block
|
||||
StreamReaderAny::pos start;
|
||||
|
||||
|
@ -561,66 +550,55 @@ struct FileBlockHead
|
|||
// number of structure instances to follow
|
||||
size_t num;
|
||||
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// for std::upper_bound
|
||||
operator const Pointer& () const {
|
||||
operator const Pointer &() const {
|
||||
return address;
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** Utility to read all master file blocks in turn. */
|
||||
// -------------------------------------------------------------------------------
|
||||
class SectionParser
|
||||
{
|
||||
class SectionParser {
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** @param stream Inout stream, must point to the
|
||||
* first section in the file. Call Next() once
|
||||
* to have it read.
|
||||
* @param ptr64 Pointer size in file is 64 bits? */
|
||||
SectionParser(StreamReaderAny& stream,bool ptr64)
|
||||
: stream(stream)
|
||||
, ptr64(ptr64)
|
||||
{
|
||||
SectionParser(StreamReaderAny &stream, bool ptr64) :
|
||||
stream(stream), ptr64(ptr64) {
|
||||
current.size = current.start = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
const FileBlockHead& GetCurrent() const {
|
||||
const FileBlockHead &GetCurrent() const {
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Advance to the next section.
|
||||
* @throw DeadlyImportError if the last chunk was passed. */
|
||||
void Next();
|
||||
|
||||
public:
|
||||
|
||||
FileBlockHead current;
|
||||
StreamReaderAny& stream;
|
||||
StreamReaderAny &stream;
|
||||
bool ptr64;
|
||||
};
|
||||
|
||||
|
||||
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
||||
// -------------------------------------------------------------------------------
|
||||
/** Import statistics, i.e. number of file blocks read*/
|
||||
|
@ -628,17 +606,13 @@ public:
|
|||
class Statistics {
|
||||
|
||||
public:
|
||||
|
||||
Statistics ()
|
||||
: fields_read ()
|
||||
, pointers_resolved ()
|
||||
, cache_hits ()
|
||||
// , blocks_read ()
|
||||
, cached_objects ()
|
||||
{}
|
||||
Statistics() :
|
||||
fields_read(), pointers_resolved(), cache_hits()
|
||||
// , blocks_read ()
|
||||
,
|
||||
cached_objects() {}
|
||||
|
||||
public:
|
||||
|
||||
/** total number of fields we read */
|
||||
unsigned int fields_read;
|
||||
|
||||
|
@ -662,17 +636,13 @@ public:
|
|||
* avoids circular references and avoids object duplication. */
|
||||
// -------------------------------------------------------------------------------
|
||||
template <template <typename> class TOUT>
|
||||
class ObjectCache
|
||||
{
|
||||
class ObjectCache {
|
||||
public:
|
||||
|
||||
typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
|
||||
typedef std::map<Pointer, TOUT<ElemBase>> StructureCache;
|
||||
|
||||
public:
|
||||
|
||||
ObjectCache(const FileDatabase& db)
|
||||
: db(db)
|
||||
{
|
||||
ObjectCache(const FileDatabase &db) :
|
||||
db(db) {
|
||||
// currently there are only ~400 structure records per blend file.
|
||||
// we read only a small part of them and don't cache objects
|
||||
// which we don't need, so this should suffice.
|
||||
|
@ -680,17 +650,17 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Check whether a specific item is in the cache.
|
||||
* @param s Data type of the item
|
||||
* @param out Output pointer. Unchanged if the
|
||||
* cache doesn't know the item yet.
|
||||
* @param ptr Item address to look for. */
|
||||
template <typename T> void get (
|
||||
const Structure& s,
|
||||
TOUT<T>& out,
|
||||
const Pointer& ptr) const;
|
||||
template <typename T>
|
||||
void get(
|
||||
const Structure &s,
|
||||
TOUT<T> &out,
|
||||
const Pointer &ptr) const;
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Add an item to the cache after the item has
|
||||
|
@ -700,47 +670,44 @@ public:
|
|||
* @param s Data type of the item
|
||||
* @param out Item to insert into the cache
|
||||
* @param ptr address (cache key) of the item. */
|
||||
template <typename T> void set
|
||||
(const Structure& s,
|
||||
const TOUT<T>& out,
|
||||
const Pointer& ptr);
|
||||
template <typename T>
|
||||
void set(const Structure &s,
|
||||
const TOUT<T> &out,
|
||||
const Pointer &ptr);
|
||||
|
||||
private:
|
||||
|
||||
mutable vector<StructureCache> caches;
|
||||
const FileDatabase& db;
|
||||
const FileDatabase &db;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------
|
||||
template <> class ObjectCache<Blender::vector>
|
||||
{
|
||||
template <>
|
||||
class ObjectCache<Blender::vector> {
|
||||
public:
|
||||
ObjectCache(const FileDatabase &) {}
|
||||
|
||||
ObjectCache(const FileDatabase&) {}
|
||||
|
||||
template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
|
||||
template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
|
||||
template <typename T>
|
||||
void get(const Structure &, vector<T> &, const Pointer &) {}
|
||||
template <typename T>
|
||||
void set(const Structure &, const vector<T> &, const Pointer &) {}
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable:4355)
|
||||
#pragma warning(disable : 4355)
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** Memory representation of a full BLEND file and all its dependencies. The
|
||||
* output aiScene is constructed from an instance of this data structure. */
|
||||
// -------------------------------------------------------------------------------
|
||||
class FileDatabase
|
||||
{
|
||||
template <template <typename> class TOUT> friend class ObjectCache;
|
||||
class FileDatabase {
|
||||
template <template <typename> class TOUT>
|
||||
friend class ObjectCache;
|
||||
|
||||
public:
|
||||
FileDatabase()
|
||||
: _cacheArrays(*this)
|
||||
, _cache(*this)
|
||||
, next_cache_idx()
|
||||
{}
|
||||
FileDatabase() :
|
||||
_cacheArrays(*this), _cache(*this), next_cache_idx() {}
|
||||
|
||||
public:
|
||||
// publicly accessible fields
|
||||
|
@ -748,12 +715,11 @@ public:
|
|||
bool little;
|
||||
|
||||
DNA dna;
|
||||
std::shared_ptr< StreamReaderAny > reader;
|
||||
vector< FileBlockHead > entries;
|
||||
std::shared_ptr<StreamReaderAny> reader;
|
||||
vector<FileBlockHead> entries;
|
||||
|
||||
public:
|
||||
|
||||
Statistics& stats() const {
|
||||
Statistics &stats() const {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
|
@ -762,18 +728,16 @@ public:
|
|||
// arrays of objects are never cached because we can't easily
|
||||
// ensure their proper destruction.
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ObjectCache<vector>& cache(vector<T>& /*in*/) const {
|
||||
ObjectCache<vector> &cache(vector<T> & /*in*/) const {
|
||||
return _cacheArrays;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
|
||||
mutable Statistics _stats;
|
||||
#endif
|
||||
|
@ -785,24 +749,20 @@ private:
|
|||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(default:4355)
|
||||
#pragma warning(default : 4355)
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
|
||||
// -------------------------------------------------------------------------------
|
||||
class DNAParser
|
||||
{
|
||||
class DNAParser {
|
||||
|
||||
public:
|
||||
|
||||
/** Bind the parser to a empty DNA and an input stream */
|
||||
DNAParser(FileDatabase& db)
|
||||
: db(db)
|
||||
{}
|
||||
DNAParser(FileDatabase &db) :
|
||||
db(db) {}
|
||||
|
||||
public:
|
||||
|
||||
// --------------------------------------------------------
|
||||
/** Locate the DNA in the file and parse it. The input
|
||||
* stream is expected to point to the beginning of the DN1
|
||||
|
@ -811,18 +771,16 @@ public:
|
|||
* @throw DeadlyImportError if the DNA cannot be read.
|
||||
* @note The position of the stream pointer is undefined
|
||||
* afterwards.*/
|
||||
void Parse ();
|
||||
void Parse();
|
||||
|
||||
public:
|
||||
|
||||
/** Obtain a reference to the extracted DNA information */
|
||||
const Blender::DNA& GetDNA() const {
|
||||
const Blender::DNA &GetDNA() const {
|
||||
return db.dna;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
} // end Blend
|
||||
} // end Assimp
|
||||
} // namespace Blender
|
||||
} // namespace Assimp
|
||||
|
||||
#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
|
||||
{
|
||||
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_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);
|
||||
cnt++;
|
||||
|
||||
curgod = NULL;
|
||||
curgod = nullptr;
|
||||
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.
|
||||
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];
|
||||
for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
|
||||
std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
|
|
@ -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
|
|
@ -55,7 +55,6 @@ namespace Blender {
|
|||
// declared in the ./source/blender/makesdna directory.
|
||||
// Stuff that is not used by Assimp is commented.
|
||||
|
||||
|
||||
// NOTE
|
||||
// this file serves as input data to the `./scripts/genblenddna.py`
|
||||
// script. This script generates the actual binding code to read a
|
||||
|
@ -95,13 +94,13 @@ namespace Blender {
|
|||
|
||||
// warn if field is missing, substitute default value
|
||||
#ifdef WARN
|
||||
# undef WARN
|
||||
#undef WARN
|
||||
#endif
|
||||
#define WARN
|
||||
|
||||
// fail the import if the field does not exist
|
||||
#ifdef FAIL
|
||||
# undef FAIL
|
||||
#undef FAIL
|
||||
#endif
|
||||
#define FAIL
|
||||
|
||||
|
@ -117,7 +116,7 @@ static const size_t MaxNameLen = 1024;
|
|||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct ID : ElemBase {
|
||||
char name[ MaxNameLen ] WARN;
|
||||
char name[MaxNameLen] WARN;
|
||||
short flag;
|
||||
};
|
||||
|
||||
|
@ -127,17 +126,16 @@ struct ListBase : ElemBase {
|
|||
std::shared_ptr<ElemBase> last;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct PackedFile : ElemBase {
|
||||
int size WARN;
|
||||
int seek WARN;
|
||||
std::shared_ptr< FileOffset > data WARN;
|
||||
std::shared_ptr<FileOffset> data WARN;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct GroupObject : ElemBase {
|
||||
std::shared_ptr<GroupObject> prev,next FAIL;
|
||||
std::shared_ptr<GroupObject> prev, next FAIL;
|
||||
std::shared_ptr<Object> ob;
|
||||
};
|
||||
|
||||
|
@ -162,11 +160,8 @@ struct MVert : ElemBase {
|
|||
int mat_nr WARN;
|
||||
int bweight;
|
||||
|
||||
MVert() : ElemBase()
|
||||
, flag(0)
|
||||
, mat_nr(0)
|
||||
, bweight(0)
|
||||
{}
|
||||
MVert() :
|
||||
ElemBase(), flag(0), mat_nr(0), bweight(0) {}
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
|
@ -203,19 +198,19 @@ struct MPoly : ElemBase {
|
|||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct MTexPoly : ElemBase {
|
||||
Image* tpage;
|
||||
Image *tpage;
|
||||
char flag, transp;
|
||||
short mode, tile, pad;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct MCol : ElemBase {
|
||||
char r,g,b,a FAIL;
|
||||
char r, g, b, a FAIL;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct MFace : ElemBase {
|
||||
int v1,v2,v3,v4 FAIL;
|
||||
int v1, v2, v3, v4 FAIL;
|
||||
int mat_nr FAIL;
|
||||
char flag;
|
||||
};
|
||||
|
@ -232,12 +227,8 @@ struct TFace : ElemBase {
|
|||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct MTFace : ElemBase {
|
||||
MTFace()
|
||||
: flag(0)
|
||||
, mode(0)
|
||||
, tile(0)
|
||||
, unwrap(0)
|
||||
{
|
||||
MTFace() :
|
||||
flag(0), mode(0), tile(0), unwrap(0) {
|
||||
}
|
||||
|
||||
float uv[4][2] FAIL;
|
||||
|
@ -270,11 +261,11 @@ struct MDeformVert : ElemBase {
|
|||
struct Material : ElemBase {
|
||||
ID id FAIL;
|
||||
|
||||
float r,g,b WARN;
|
||||
float specr,specg,specb WARN;
|
||||
float r, g, b WARN;
|
||||
float specr, specg, specb WARN;
|
||||
short har;
|
||||
float ambr,ambg,ambb WARN;
|
||||
float mirr,mirg,mirb;
|
||||
float ambr, ambg, ambb WARN;
|
||||
float mirr, mirg, mirb;
|
||||
float emit WARN;
|
||||
float ray_mirror;
|
||||
float alpha WARN;
|
||||
|
@ -401,18 +392,17 @@ struct CustomDataLayer : ElemBase {
|
|||
char name[64];
|
||||
std::shared_ptr<ElemBase> data; // must be converted to real type according type member
|
||||
|
||||
CustomDataLayer()
|
||||
: ElemBase()
|
||||
, type(0)
|
||||
, offset(0)
|
||||
, flag(0)
|
||||
, active(0)
|
||||
, active_rnd(0)
|
||||
, active_clone(0)
|
||||
, active_mask(0)
|
||||
, uid(0)
|
||||
, data(nullptr)
|
||||
{
|
||||
CustomDataLayer() :
|
||||
ElemBase(),
|
||||
type(0),
|
||||
offset(0),
|
||||
flag(0),
|
||||
active(0),
|
||||
active_rnd(0),
|
||||
active_clone(0),
|
||||
active_mask(0),
|
||||
uid(0),
|
||||
data(nullptr) {
|
||||
memset(name, 0, sizeof name);
|
||||
}
|
||||
};
|
||||
|
@ -430,7 +420,7 @@ CustomData 208
|
|||
CustomDataExternal *external 200 8
|
||||
*/
|
||||
struct CustomData : ElemBase {
|
||||
vector<std::shared_ptr<struct CustomDataLayer> > layers;
|
||||
vector<std::shared_ptr<struct CustomDataLayer>> layers;
|
||||
int typemap[42]; // CD_NUMTYPES
|
||||
int totlayer;
|
||||
int maxlayer;
|
||||
|
@ -469,7 +459,7 @@ struct Mesh : ElemBase {
|
|||
vector<MDeformVert> dvert;
|
||||
vector<MCol> mcol;
|
||||
|
||||
vector< std::shared_ptr<Material> > mat FAIL;
|
||||
vector<std::shared_ptr<Material>> mat FAIL;
|
||||
|
||||
struct CustomData vdata;
|
||||
struct CustomData edata;
|
||||
|
@ -490,36 +480,35 @@ struct Library : ElemBase {
|
|||
// -------------------------------------------------------------------------------
|
||||
struct Camera : ElemBase {
|
||||
enum Type {
|
||||
Type_PERSP = 0
|
||||
,Type_ORTHO = 1
|
||||
Type_PERSP = 0,
|
||||
Type_ORTHO = 1
|
||||
};
|
||||
|
||||
ID id FAIL;
|
||||
|
||||
Type type,flag WARN;
|
||||
Type type, flag WARN;
|
||||
float lens WARN;
|
||||
float sensor_x WARN;
|
||||
float clipsta, clipend;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct Lamp : ElemBase {
|
||||
|
||||
enum FalloffType {
|
||||
FalloffType_Constant = 0x0
|
||||
,FalloffType_InvLinear = 0x1
|
||||
,FalloffType_InvSquare = 0x2
|
||||
FalloffType_Constant = 0x0,
|
||||
FalloffType_InvLinear = 0x1,
|
||||
FalloffType_InvSquare = 0x2
|
||||
//,FalloffType_Curve = 0x3
|
||||
//,FalloffType_Sliders = 0x4
|
||||
};
|
||||
|
||||
enum Type {
|
||||
Type_Local = 0x0
|
||||
,Type_Sun = 0x1
|
||||
,Type_Spot = 0x2
|
||||
,Type_Hemi = 0x3
|
||||
,Type_Area = 0x4
|
||||
Type_Local = 0x0,
|
||||
Type_Sun = 0x1,
|
||||
Type_Spot = 0x2,
|
||||
Type_Hemi = 0x3,
|
||||
Type_Area = 0x4
|
||||
//,Type_YFPhoton = 0x5
|
||||
};
|
||||
|
||||
|
@ -532,7 +521,7 @@ struct Lamp : ElemBase {
|
|||
//int mode;
|
||||
|
||||
short colormodel, totex;
|
||||
float r,g,b,k WARN;
|
||||
float r, g, b, k WARN;
|
||||
//float shdwr, shdwg, shdwb;
|
||||
|
||||
float energy, dist, spotsize, spotblend;
|
||||
|
@ -646,13 +635,13 @@ struct SubsurfModifierData : ElemBase {
|
|||
|
||||
enum Flags {
|
||||
// some omitted
|
||||
FLAGS_SubsurfUV =1<<3
|
||||
FLAGS_SubsurfUV = 1 << 3
|
||||
};
|
||||
|
||||
ModifierData modifier FAIL;
|
||||
short subdivType WARN;
|
||||
short levels FAIL;
|
||||
short renderLevels ;
|
||||
short renderLevels;
|
||||
short flags;
|
||||
};
|
||||
|
||||
|
@ -660,13 +649,13 @@ struct SubsurfModifierData : ElemBase {
|
|||
struct MirrorModifierData : ElemBase {
|
||||
|
||||
enum Flags {
|
||||
Flags_CLIPPING =1<<0,
|
||||
Flags_MIRROR_U =1<<1,
|
||||
Flags_MIRROR_V =1<<2,
|
||||
Flags_AXIS_X =1<<3,
|
||||
Flags_AXIS_Y =1<<4,
|
||||
Flags_AXIS_Z =1<<5,
|
||||
Flags_VGROUP =1<<6
|
||||
Flags_CLIPPING = 1 << 0,
|
||||
Flags_MIRROR_U = 1 << 1,
|
||||
Flags_MIRROR_V = 1 << 2,
|
||||
Flags_AXIS_X = 1 << 3,
|
||||
Flags_AXIS_Y = 1 << 4,
|
||||
Flags_AXIS_Z = 1 << 5,
|
||||
Flags_VGROUP = 1 << 6
|
||||
};
|
||||
|
||||
ModifierData modifier FAIL;
|
||||
|
@ -681,18 +670,20 @@ struct Object : ElemBase {
|
|||
ID id FAIL;
|
||||
|
||||
enum Type {
|
||||
Type_EMPTY = 0
|
||||
,Type_MESH = 1
|
||||
,Type_CURVE = 2
|
||||
,Type_SURF = 3
|
||||
,Type_FONT = 4
|
||||
,Type_MBALL = 5
|
||||
Type_EMPTY = 0,
|
||||
Type_MESH = 1,
|
||||
Type_CURVE = 2,
|
||||
Type_SURF = 3,
|
||||
Type_FONT = 4,
|
||||
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;
|
||||
|
@ -700,39 +691,29 @@ struct Object : ElemBase {
|
|||
float parentinv[4][4] WARN;
|
||||
char parsubstr[32] WARN;
|
||||
|
||||
Object* parent WARN;
|
||||
Object *parent 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<ElemBase> data FAIL;
|
||||
|
||||
ListBase modifiers;
|
||||
|
||||
Object()
|
||||
: ElemBase()
|
||||
, type( Type_EMPTY )
|
||||
, parent( nullptr )
|
||||
, track()
|
||||
, proxy()
|
||||
, proxy_from()
|
||||
, data() {
|
||||
Object() :
|
||||
ElemBase(), type(Type_EMPTY), parent(nullptr), track(), proxy(), proxy_from(), data() {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
struct Base : ElemBase {
|
||||
Base* prev WARN;
|
||||
Base *prev WARN;
|
||||
std::shared_ptr<Base> next WARN;
|
||||
std::shared_ptr<Object> object WARN;
|
||||
|
||||
Base()
|
||||
: ElemBase()
|
||||
, prev( nullptr )
|
||||
, next()
|
||||
, object() {
|
||||
Base() :
|
||||
ElemBase(), prev(nullptr), next(), object() {
|
||||
// empty
|
||||
// empty
|
||||
}
|
||||
|
@ -748,11 +729,8 @@ struct Scene : ElemBase {
|
|||
|
||||
ListBase base;
|
||||
|
||||
Scene()
|
||||
: ElemBase()
|
||||
, camera()
|
||||
, world()
|
||||
, basact() {
|
||||
Scene() :
|
||||
ElemBase(), camera(), world(), basact() {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
@ -784,8 +762,8 @@ struct Image : ElemBase {
|
|||
|
||||
short gen_x, gen_y, gen_type;
|
||||
|
||||
Image()
|
||||
: ElemBase() {
|
||||
Image() :
|
||||
ElemBase() {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
@ -795,33 +773,33 @@ struct Tex : ElemBase {
|
|||
|
||||
// actually, the only texture type we support is Type_IMAGE
|
||||
enum Type {
|
||||
Type_CLOUDS = 1
|
||||
,Type_WOOD = 2
|
||||
,Type_MARBLE = 3
|
||||
,Type_MAGIC = 4
|
||||
,Type_BLEND = 5
|
||||
,Type_STUCCI = 6
|
||||
,Type_NOISE = 7
|
||||
,Type_IMAGE = 8
|
||||
,Type_PLUGIN = 9
|
||||
,Type_ENVMAP = 10
|
||||
,Type_MUSGRAVE = 11
|
||||
,Type_VORONOI = 12
|
||||
,Type_DISTNOISE = 13
|
||||
,Type_POINTDENSITY = 14
|
||||
,Type_VOXELDATA = 15
|
||||
Type_CLOUDS = 1,
|
||||
Type_WOOD = 2,
|
||||
Type_MARBLE = 3,
|
||||
Type_MAGIC = 4,
|
||||
Type_BLEND = 5,
|
||||
Type_STUCCI = 6,
|
||||
Type_NOISE = 7,
|
||||
Type_IMAGE = 8,
|
||||
Type_PLUGIN = 9,
|
||||
Type_ENVMAP = 10,
|
||||
Type_MUSGRAVE = 11,
|
||||
Type_VORONOI = 12,
|
||||
Type_DISTNOISE = 13,
|
||||
Type_POINTDENSITY = 14,
|
||||
Type_VOXELDATA = 15
|
||||
};
|
||||
|
||||
enum ImageFlags {
|
||||
ImageFlags_INTERPOL = 1
|
||||
,ImageFlags_USEALPHA = 2
|
||||
,ImageFlags_MIPMAP = 4
|
||||
,ImageFlags_IMAROT = 16
|
||||
,ImageFlags_CALCALPHA = 32
|
||||
,ImageFlags_NORMALMAP = 2048
|
||||
,ImageFlags_GAUSS_MIP = 4096
|
||||
,ImageFlags_FILTER_MIN = 8192
|
||||
,ImageFlags_DERIVATIVEMAP = 16384
|
||||
ImageFlags_INTERPOL = 1,
|
||||
ImageFlags_USEALPHA = 2,
|
||||
ImageFlags_MIPMAP = 4,
|
||||
ImageFlags_IMAROT = 16,
|
||||
ImageFlags_CALCALPHA = 32,
|
||||
ImageFlags_NORMALMAP = 2048,
|
||||
ImageFlags_GAUSS_MIP = 4096,
|
||||
ImageFlags_FILTER_MIN = 8192,
|
||||
ImageFlags_DERIVATIVEMAP = 16384
|
||||
};
|
||||
|
||||
ID id FAIL;
|
||||
|
@ -876,11 +854,8 @@ struct Tex : ElemBase {
|
|||
|
||||
//char use_nodes;
|
||||
|
||||
Tex()
|
||||
: ElemBase()
|
||||
, imaflag( ImageFlags_INTERPOL )
|
||||
, type( Type_CLOUDS )
|
||||
, ima() {
|
||||
Tex() :
|
||||
ElemBase(), imaflag(ImageFlags_INTERPOL), type(Type_CLOUDS), ima() {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
@ -889,52 +864,52 @@ struct Tex : ElemBase {
|
|||
struct MTex : ElemBase {
|
||||
|
||||
enum Projection {
|
||||
Proj_N = 0
|
||||
,Proj_X = 1
|
||||
,Proj_Y = 2
|
||||
,Proj_Z = 3
|
||||
Proj_N = 0,
|
||||
Proj_X = 1,
|
||||
Proj_Y = 2,
|
||||
Proj_Z = 3
|
||||
};
|
||||
|
||||
enum Flag {
|
||||
Flag_RGBTOINT = 0x1
|
||||
,Flag_STENCIL = 0x2
|
||||
,Flag_NEGATIVE = 0x4
|
||||
,Flag_ALPHAMIX = 0x8
|
||||
,Flag_VIEWSPACE = 0x10
|
||||
Flag_RGBTOINT = 0x1,
|
||||
Flag_STENCIL = 0x2,
|
||||
Flag_NEGATIVE = 0x4,
|
||||
Flag_ALPHAMIX = 0x8,
|
||||
Flag_VIEWSPACE = 0x10
|
||||
};
|
||||
|
||||
enum BlendType {
|
||||
BlendType_BLEND = 0
|
||||
,BlendType_MUL = 1
|
||||
,BlendType_ADD = 2
|
||||
,BlendType_SUB = 3
|
||||
,BlendType_DIV = 4
|
||||
,BlendType_DARK = 5
|
||||
,BlendType_DIFF = 6
|
||||
,BlendType_LIGHT = 7
|
||||
,BlendType_SCREEN = 8
|
||||
,BlendType_OVERLAY = 9
|
||||
,BlendType_BLEND_HUE = 10
|
||||
,BlendType_BLEND_SAT = 11
|
||||
,BlendType_BLEND_VAL = 12
|
||||
,BlendType_BLEND_COLOR = 13
|
||||
BlendType_BLEND = 0,
|
||||
BlendType_MUL = 1,
|
||||
BlendType_ADD = 2,
|
||||
BlendType_SUB = 3,
|
||||
BlendType_DIV = 4,
|
||||
BlendType_DARK = 5,
|
||||
BlendType_DIFF = 6,
|
||||
BlendType_LIGHT = 7,
|
||||
BlendType_SCREEN = 8,
|
||||
BlendType_OVERLAY = 9,
|
||||
BlendType_BLEND_HUE = 10,
|
||||
BlendType_BLEND_SAT = 11,
|
||||
BlendType_BLEND_VAL = 12,
|
||||
BlendType_BLEND_COLOR = 13
|
||||
};
|
||||
|
||||
enum MapType {
|
||||
MapType_COL = 1
|
||||
,MapType_NORM = 2
|
||||
,MapType_COLSPEC = 4
|
||||
,MapType_COLMIR = 8
|
||||
,MapType_REF = 16
|
||||
,MapType_SPEC = 32
|
||||
,MapType_EMIT = 64
|
||||
,MapType_ALPHA = 128
|
||||
,MapType_HAR = 256
|
||||
,MapType_RAYMIRR = 512
|
||||
,MapType_TRANSLU = 1024
|
||||
,MapType_AMB = 2048
|
||||
,MapType_DISPLACE = 4096
|
||||
,MapType_WARP = 8192
|
||||
MapType_COL = 1,
|
||||
MapType_NORM = 2,
|
||||
MapType_COLSPEC = 4,
|
||||
MapType_COLMIR = 8,
|
||||
MapType_REF = 16,
|
||||
MapType_SPEC = 32,
|
||||
MapType_EMIT = 64,
|
||||
MapType_ALPHA = 128,
|
||||
MapType_HAR = 256,
|
||||
MapType_RAYMIRR = 512,
|
||||
MapType_TRANSLU = 1024,
|
||||
MapType_AMB = 2048,
|
||||
MapType_DISPLACE = 4096,
|
||||
MapType_WARP = 8192
|
||||
};
|
||||
|
||||
// short texco, maptoneg;
|
||||
|
@ -945,7 +920,7 @@ struct MTex : ElemBase {
|
|||
std::shared_ptr<Tex> tex;
|
||||
char uvname[32];
|
||||
|
||||
Projection projx,projy,projz;
|
||||
Projection projx, projy, projz;
|
||||
char mapping;
|
||||
float ofs[3], size[3], rot;
|
||||
|
||||
|
@ -953,7 +928,7 @@ struct MTex : ElemBase {
|
|||
short colormodel, pmapto, pmaptoneg;
|
||||
//short normapspace, which_output;
|
||||
//char brush_map_mode;
|
||||
float r,g,b,k WARN;
|
||||
float r, g, b, k WARN;
|
||||
//float def_var, rt;
|
||||
|
||||
//float colfac, varfac;
|
||||
|
@ -972,12 +947,12 @@ struct MTex : ElemBase {
|
|||
//float shadowfac;
|
||||
//float zenupfac, zendownfac, blendfac;
|
||||
|
||||
MTex()
|
||||
: ElemBase() {
|
||||
MTex() :
|
||||
ElemBase() {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace Blender
|
||||
} // namespace Assimp
|
||||
#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
|
||||
|
||||
#include "DXF/DXFLoader.h"
|
||||
#include "DXF/DXFHelper.h"
|
||||
#include "AssetLib/DXF/DXFLoader.h"
|
||||
#include "AssetLib/DXF/DXFHelper.h"
|
||||
#include "PostProcessing/ConvertToLHProcess.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