Merge remote-tracking branch 'upstream/master' into feature/fix-glTF-validator-errors

pull/1015/head
Angelo Scandaliato 2016-10-03 09:34:16 -07:00
commit 99c93e861c
40 changed files with 11351 additions and 210 deletions

View File

@ -15,6 +15,7 @@ Coverity<a href="https://scan.coverity.com/projects/5607">
src="https://scan.coverity.com/projects/5607/badge.svg"/>
</a>
<br>
Gitter chat: [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
__[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.__
#### Supported file formats ####

View File

@ -0,0 +1,661 @@
/// \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 "fast_atof.h"
#include "DefaultIOSystem.h"
// Header files, stdlib.
#include <memory>
#include <string>
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.size())
{
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)
{
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;
LogWarning("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;
// 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;
// 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;
// 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;
// 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;
// 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[] = { "<?xml", "<amf" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
}
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

View File

@ -0,0 +1,521 @@
/// \file AMFImporter.hpp
/// \brief AMF-format files importer for Assimp.
/// \date 2016
/// \author smal.root@gmail.com
// Thanks to acorn89 for support.
#ifndef INCLUDED_AI_AMF_IMPORTER_H
#define INCLUDED_AI_AMF_IMPORTER_H
#include "AMFImporter_Node.hpp"
// Header files, Assimp.
#include "assimp/DefaultLogger.hpp"
#include "assimp/importerdesc.h"
#include "assimp/ProgressHandler.hpp"
#include "assimp/types.h"
#include "BaseImporter.h"
#include "irrXMLWrapper.h"
// Header files, stdlib.
#include <set>
namespace Assimp
{
/// \class AMFImporter
/// Class that holding scene graph which include: geometry, metadata, materials etc.
///
/// Implementing features.
///
/// Limitations.
///
/// 1. When for texture mapping used set of source textures (r, g, b, a) not only one then attribute "tiled" for all set will be true if it true in any of
/// source textures.
/// Example. Triangle use for texture mapping three textures. Two of them has "tiled" set to false and one - set to true. In scene all three textures
/// will be tiled.
///
/// Unsupported features:
/// 1. Node <composite>, formulas in <composite> and <color>. For implementing this feature can be used expression parser "muParser" like in project
/// "amf_tools".
/// 2. Attribute "profile" in node <color>.
/// 3. Curved geometry: <edge>, <normal> and children nodes of them.
/// 4. Attributes: "unit" and "version" in <amf> read but do nothing.
/// 5. <metadata> stored only for root node <amf>.
/// 6. Color averaging of vertices for which <triangle>'s set different colors.
///
/// Supported nodes:
/// General:
/// <amf>; <constellation>; <instance> and children <deltax>, <deltay>, <deltaz>, <rx>, <ry>, <rz>; <metadata>;
///
/// Geometry:
/// <object>; <mesh>; <vertices>; <vertex>; <coordinates> and children <x>, <y>, <z>; <volume>; <triangle> and children <v1>, <v2>, <v3>;
///
/// Material:
/// <color> and children <r>, <g>, <b>, <a>; <texture>; <material>;
/// two variants of texture coordinates:
/// new - <texmap> and children <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>
/// old - <map> and children <u1>, <u2>, <u3>, <v1>, <v2>, <v3>
///
class AMFImporter : public BaseImporter
{
/***********************************************/
/******************** Types ********************/
/***********************************************/
private:
struct SPP_Material;// forward declaration
/// \struct SPP_Composite
/// Data type for postprocessing step. More suitable container for part of material's composition.
struct SPP_Composite
{
SPP_Material* Material;///< Pointer to material - part of composition.
std::string Formula;///< Formula for calculating ratio of \ref Material.
};
/// \struct SPP_Material
/// Data type for postprocessing step. More suitable container for material.
struct SPP_Material
{
std::string ID;///< Material ID.
std::list<CAMFImporter_NodeElement_Metadata*> Metadata;///< Metadata of material.
CAMFImporter_NodeElement_Color* Color;///< Color of material.
std::list<SPP_Composite> Composition;///< List of child materials if current material is composition of few another.
/// \fn aiColor4D GetColor(const float pX, const float pY, const float pZ) const
/// Return color calculated for specified coordinate.
/// \param [in] pX - "x" coordinate.
/// \param [in] pY - "y" coordinate.
/// \param [in] pZ - "z" coordinate.
/// \return calculated color.
aiColor4D GetColor(const float pX, const float pY, const float pZ) const;
};
/// \struct SPP_Texture
/// Data type for postprocessing step. More suitable container for texture.
struct SPP_Texture
{
std::string ID;
size_t Width, Height, Depth;
bool Tiled;
decltype(aiTexture::achFormatHint) FormatHint;
uint8_t* Data;
};
/// \struct SComplexFace
/// Data type for postprocessing step. Contain face data.
struct SComplexFace
{
aiFace Face;///< Face vertices.
const CAMFImporter_NodeElement_Color* Color;///< Face color. Equal to nullptr if color is not set for the face.
const CAMFImporter_NodeElement_TexMap* TexMap;///< Face texture mapping data. Equal to nullptr if texture mapping is not set for the face.
};
/***********************************************/
/****************** Constants ******************/
/***********************************************/
private:
static const aiImporterDesc Description;
/***********************************************/
/****************** Variables ******************/
/***********************************************/
private:
CAMFImporter_NodeElement* mNodeElement_Cur;///< Current element.
std::list<CAMFImporter_NodeElement*> mNodeElement_List;///< All elements of scene graph.
irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
std::string mUnit;
std::list<SPP_Material> mMaterial_Converted;///< List of converted materials for postprocessing step.
std::list<SPP_Texture> mTexture_Converted;///< List of converted textures for postprocessing step.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn AMFImporter(const AMFImporter& pScene)
/// Disabled copy constructor.
AMFImporter(const AMFImporter& pScene);
/// \fn AMFImporter& operator=(const AMFImporter& pScene)
/// Disabled assign operator.
AMFImporter& operator=(const AMFImporter& pScene);
/// \fn void Clear()
/// Clear all temporary data.
void Clear();
/***********************************************/
/************* Functions: find set *************/
/***********************************************/
/// \fn bool Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, aiNode** pNode) const
/// Find specified node element in node elements list ( \ref mNodeElement_List).
/// \param [in] pID - ID(name) of requested node element.
/// \param [in] pType - type of node element.
/// \param [out] pNode - pointer to pointer to item found.
/// \return true - if the node element is found, else - false.
bool Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const;
/// \fn bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
/// Find requested aiNode in node list.
/// \param [in] pID - ID(name) of requested node.
/// \param [in] pNodeList - list of nodes where to find the node.
/// \param [out] pNode - pointer to pointer to item found.
/// \return true - if the node is found, else - false.
bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const;
/// \fn bool Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
/// Find material in list for converted materials. Use at postprocessing step.
/// \param [in] pID - material ID.
/// \param [out] pConvertedMaterial - pointer to found converted material (\ref SPP_Material).
/// \return true - if the material is found, else - false.
bool Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const;
/// \fn bool Find_ConvertedTexture(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A, uint32_t* pConvertedTextureIndex = nullptr) const
/// Find texture in list of converted textures. Use at postprocessing step,
/// \param [in] pID_R - ID of source "red" texture.
/// \param [in] pID_G - ID of source "green" texture.
/// \param [in] pID_B - ID of source "blue" texture.
/// \param [in] pID_A - ID of source "alpha" texture. Use empty string to find RGB-texture.
/// \param [out] pConvertedTextureIndex - pointer where index in list of found texture will be written. If equivalent to nullptr then nothing will be
/// written.
/// \return true - if the texture is found, else - false.
bool Find_ConvertedTexture(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A,
uint32_t* pConvertedTextureIndex = nullptr) const;
/***********************************************/
/********* Functions: postprocess set **********/
/***********************************************/
/// \fn void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray, std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const
/// Get data stored in <vertices> and place it to arrays.
/// \param [in] pNodeElement - reference to node element which kept <object> data.
/// \param [in] pVertexCoordinateArray - reference to vertices coordinates kept in <vertices>.
/// \param [in] pVertexColorArray - reference to vertices colors for all <vertex's. If color for vertex is not set then corresponding member of array
/// contain nullptr.
void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const;
/// \fn size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A)
/// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
/// converted texture will be returned. Convertion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
/// to converted textures list.
/// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
/// \param [in] pID_R - ID of source "red" texture.
/// \param [in] pID_G - ID of source "green" texture.
/// \param [in] pID_B - ID of source "blue" texture.
/// \param [in] pID_A - ID of source "alpha" texture.
/// \return index of the texture in array of the converted textures.
size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A);
/// \fn void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> > pOutputList_Separated)
/// Separate input list by texture IDs. This step is needed because aiMesh can contain mesh which is use only one texture (or set: diffuse, bump etc).
/// \param [in] pInputList - input list with faces. Some of them can contain color or texture mapping, or both of them, or nothing. Will be cleared after
/// processing.
/// \param [out] pOutputList_Separated - output list of the faces lists. Separated faces list by used texture IDs. Will be cleared before processing.
void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated);
/// \fn void Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const
/// Check if child elements of node element is metadata and add it to scene node.
/// \param [in] pMetadataList - reference to list with collected metadata.
/// \param [out] pSceneNode - scene node in which metadata will be added.
void Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const;
/// \fn void Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode)
/// To create aiMesh and aiNode for it from <object>.
/// \param [in] pNodeElement - reference to node element which kept <object> data.
/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
/// \param [out] pSceneNode - pointer to place where new aiNode will be created.
void Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode);
/// \fn void 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)
/// Create mesh for every <volume> in <mesh>.
/// \param [in] pNodeElement - reference to node element which kept <mesh> data.
/// \param [in] pVertexCoordinateArray - reference to vertices coordinates for all <volume>'s.
/// \param [in] pVertexColorArray - reference to vertices colors for all <volume>'s. If color for vertex is not set then corresponding member of array
/// contain nullptr.
/// \param [in] pObjectColor - pointer to colors for <object>. If color is not set then argument contain nullptr.
/// \param [in] pMaterialList - reference to a list with defined materials.
/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
/// \param [out] pSceneNode - reference to aiNode which will own new aiMesh's.
void 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);
/// \fn void Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial)
/// Convert material from \ref CAMFImporter_NodeElement_Material to \ref SPP_Material.
/// \param [in] pMaterial - source CAMFImporter_NodeElement_Material.
void Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial);
/// \fn void Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const
/// Create and add to aiNode's list new part of scene graph defined by <constellation>.
/// \param [in] pConstellation - reference to <constellation> node.
/// \param [out] pNodeList - reference to aiNode's list.
void Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const;
/// \fn void Postprocess_BuildScene()
/// Build Assimp scene graph in aiScene from collected data.
/// \param [out] pScene - pointer to aiScene where tree will be built.
void Postprocess_BuildScene(aiScene* pScene);
/***********************************************/
/************* Functions: throw set ************/
/***********************************************/
/// \fn void Throw_CloseNotFound(const std::string& pNode)
/// Call that function when close tag of node not found and exception must be raised.
/// E.g.:
/// <amf>
/// <object>
/// </amf> <!--- object not closed --->
/// \throw DeadlyImportError.
/// \param [in] pNode - node name in which exception happened.
void Throw_CloseNotFound(const std::string& pNode);
/// \fn void Throw_IncorrectAttr(const std::string& pAttrName)
/// Call that function when attribute name is incorrect and exception must be raised.
/// \param [in] pAttrName - attribute name.
/// \throw DeadlyImportError.
void Throw_IncorrectAttr(const std::string& pAttrName);
/// \fn void Throw_IncorrectAttrValue(const std::string& pAttrName)
/// Call that function when attribute value is incorrect and exception must be raised.
/// \param [in] pAttrName - attribute name.
/// \throw DeadlyImportError.
void Throw_IncorrectAttrValue(const std::string& pAttrName);
/// \fn void Throw_MoreThanOnceDefined(const std::string& pNode, const std::string& pDescription)
/// Call that function when some type of nodes are defined twice or more when must be used only once and exception must be raised.
/// E.g.:
/// <object>
/// <color>... <!--- color defined --->
/// <color>... <!--- color defined again --->
/// </object>
/// \throw DeadlyImportError.
/// \param [in] pNodeType - type of node which defined one more time.
/// \param [in] pDescription - message about error. E.g. what the node defined while exception raised.
void Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription);
/// \fn void Throw_ID_NotFound(const std::string& pID) const
/// Call that function when referenced element ID are not found in graph and exception must be raised.
/// \param [in] pID - ID of of element which not found.
/// \throw DeadlyImportError.
void Throw_ID_NotFound(const std::string& pID) const;
/***********************************************/
/************** Functions: LOG set *************/
/***********************************************/
/// \fn void LogInfo(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->info()
void LogInfo(const std::string& pMessage) { DefaultLogger::get()->info(pMessage); }
/// \fn void LogWarning(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->warn()
void LogWarning(const std::string& pMessage) { DefaultLogger::get()->warn(pMessage); }
/// \fn void LogError(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->error()
void LogError(const std::string& pMessage) { DefaultLogger::get()->error(pMessage); }
/***********************************************/
/************** Functions: XML set *************/
/***********************************************/
/// \fn void XML_CheckNode_MustHaveChildren()
/// Check if current node have children: <node>...</node>. If not then exception will throwed.
void XML_CheckNode_MustHaveChildren();
/// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName)
/// Chek if current node name is equal to pNodeName.
/// \param [in] pNodeName - name for checking.
/// return true if current node name is equal to pNodeName, else - false.
bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }
/// \fn void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
/// Skip unsupported node and report about that. Depend on node name can be skipped begin tag of node all whole node.
/// \param [in] pParentNodeName - parent node name. Used for reporting.
void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName);
/// \fn bool XML_SearchNode(const std::string& pNodeName)
/// Search for specified node in file. XML file read pointer(mReader) will point to found node or file end after search is end.
/// \param [in] pNodeName - requested node name.
/// return true - if node is found, else - false.
bool XML_SearchNode(const std::string& pNodeName);
/// \fn bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
/// \fn float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx);
/// \fn uint32_t XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
uint32_t XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx);
/// \fn float XML_ReadNode_GetVal_AsFloat()
/// Read node value.
/// \return read data.
float XML_ReadNode_GetVal_AsFloat();
/// \fn uint32_t XML_ReadNode_GetVal_AsU32()
/// Read node value.
/// \return read data.
uint32_t XML_ReadNode_GetVal_AsU32();
/// \fn void XML_ReadNode_GetVal_AsString(std::string& pValue)
/// Read node value.
/// \return read data.
void XML_ReadNode_GetVal_AsString(std::string& pValue);
/***********************************************/
/******** Functions: parse set private *********/
/***********************************************/
/// \fn void ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
/// Make pNode as current and enter deeper for parsing child nodes. At end \ref ParseHelper_Node_Exit must be called.
/// \param [in] pNode - new current node.
void ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode);
/// \fn void ParseHelper_Group_End()
/// This function must be called when exiting from grouping node. \ref ParseHelper_Group_Begin.
void ParseHelper_Node_Exit();
/// \fn void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
/// Attribute values of floating point types can take form ".x"(without leading zero). irrXMLReader can not read this form of values and it
/// must be converted to right form - "0.xxx".
/// \param [in] pInStr - pointer to input string which can contain incorrect form of values.
/// \param [out[ pOutString - output string with right form of values.
void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString);
/// \fn void ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
/// Decode Base64-encoded data.
/// \param [in] pInputBase64 - reference to input Base64-encoded string.
/// \param [out] pOutputData - reference to output array for decoded data.
void ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const;
/// \fn void ParseNode_Root()
/// Parse <AMF> node of the file.
void ParseNode_Root();
/******** Functions: top nodes *********/
/// \fn void ParseNode_Constellation()
/// Parse <constellation> node of the file.
void ParseNode_Constellation();
/// \fn void ParseNode_Constellation()
/// Parse <instance> node of the file.
void ParseNode_Instance();
/// \fn void ParseNode_Material()
/// Parse <material> node of the file.
void ParseNode_Material();
/// \fn void ParseNode_Metadata()
/// Parse <metadata> node.
void ParseNode_Metadata();
/// \fn void ParseNode_Object()
/// Parse <object> node of the file.
void ParseNode_Object();
/// \fn void ParseNode_Texture()
/// Parse <texture> node of the file.
void ParseNode_Texture();
/******** Functions: geometry nodes *********/
/// \fn void ParseNode_Coordinates()
/// Parse <coordinates> node of the file.
void ParseNode_Coordinates();
/// \fn void ParseNode_Edge()
/// Parse <edge> node of the file.
void ParseNode_Edge();
/// \fn void ParseNode_Mesh()
/// Parse <mesh> node of the file.
void ParseNode_Mesh();
/// \fn void ParseNode_Triangle()
/// Parse <triangle> node of the file.
void ParseNode_Triangle();
/// \fn void ParseNode_Vertex()
/// Parse <vertex> node of the file.
void ParseNode_Vertex();
/// \fn void ParseNode_Vertices()
/// Parse <vertices> node of the file.
void ParseNode_Vertices();
/// \fn void ParseNode_Volume()
/// Parse <volume> node of the file.
void ParseNode_Volume();
/******** Functions: material nodes *********/
/// \fn void ParseNode_Color()
/// Parse <color> node of the file.
void ParseNode_Color();
/// \fn void ParseNode_TexMap(const bool pUseOldName = false)
/// Parse <texmap> of <map> node of the file.
/// \param [in] pUseOldName - if true then use old name of node(and children) - <map>, instead of new name - <texmap>.
void ParseNode_TexMap(const bool pUseOldName = false);
public:
/// \fn AMFImporter()
/// Default constructor.
AMFImporter()
: mNodeElement_Cur(nullptr), mReader(nullptr)
{}
/// \fn ~AMFImporter()
/// Default destructor.
~AMFImporter();
/***********************************************/
/******** Functions: parse set, public *********/
/***********************************************/
/// \fn void ParseFile(const std::string& pFile, IOSystem* pIOHandler)
/// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
/// Also exception can be throwed if trouble will found.
/// \param [in] pFile - name of file to be parsed.
/// \param [in] pIOHandler - pointer to IO helper object.
void ParseFile(const std::string& pFile, IOSystem* pIOHandler);
/***********************************************/
/********* Functions: BaseImporter set *********/
/***********************************************/
bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const;
void GetExtensionList(std::set<std::string>& pExtensionList);
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
const aiImporterDesc* GetInfo ()const;
};// class AMFImporter
}// namespace Assimp
#endif // INCLUDED_AI_AMF_IMPORTER_H

View File

@ -0,0 +1,314 @@
/// \file AMFImporter_Geometry.cpp
/// \brief Parsing data from geometry nodes.
/// \date 2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
#include "AMFImporter.hpp"
#include "AMFImporter_Macro.hpp"
namespace Assimp
{
// <mesh>
// </mesh>
// A 3D mesh hull.
// Multi elements - Yes.
// Parent element - <object>.
void AMFImporter::ParseNode_Mesh()
{
CAMFImporter_NodeElement* ne;
// create new mesh object.
ne = new CAMFImporter_NodeElement_Mesh(mNodeElement_Cur);
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool vert_read = false;
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("mesh");
if(XML_CheckNode_NameEqual("vertices"))
{
// Check if data already defined.
if(vert_read) Throw_MoreThanOnceDefined("vertices", "Only one vertices set can be defined for <mesh>.");
// read data and set flag about it
ParseNode_Vertices();
vert_read = true;
continue;
}
if(XML_CheckNode_NameEqual("volume")) { ParseNode_Volume(); continue; }
MACRO_NODECHECK_LOOPEND("mesh");
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.
}
// <vertices>
// </vertices>
// The list of vertices to be used in defining triangles.
// Multi elements - No.
// Parent element - <mesh>.
void AMFImporter::ParseNode_Vertices()
{
CAMFImporter_NodeElement* ne;
// create new mesh object.
ne = new CAMFImporter_NodeElement_Vertices(mNodeElement_Cur);
// Check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("vertices");
if(XML_CheckNode_NameEqual("vertex")) { ParseNode_Vertex(); continue; }
MACRO_NODECHECK_LOOPEND("vertices");
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.
}
// <vertex>
// </vertex>
// A vertex to be referenced in triangles.
// Multi elements - Yes.
// Parent element - <vertices>.
void AMFImporter::ParseNode_Vertex()
{
CAMFImporter_NodeElement* ne;
// create new mesh object.
ne = new CAMFImporter_NodeElement_Vertex(mNodeElement_Cur);
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool col_read = false;
bool coord_read = false;
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("vertex");
if(XML_CheckNode_NameEqual("color"))
{
// Check if data already defined.
if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <vertex>.");
// read data and set flag about it
ParseNode_Color();
col_read = true;
continue;
}
if(XML_CheckNode_NameEqual("coordinates"))
{
// Check if data already defined.
if(coord_read) Throw_MoreThanOnceDefined("coordinates", "Only one coordinates set can be defined for <vertex>.");
// read data and set flag about it
ParseNode_Coordinates();
coord_read = true;
continue;
}
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
MACRO_NODECHECK_LOOPEND("vertex");
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.
}
// <coordinates>
// </coordinates>
// Specifies the 3D location of this vertex.
// Multi elements - No.
// Parent element - <vertex>.
//
// Children elements:
// <x>, <y>, <z>
// Multi elements - No.
// X, Y, or Z coordinate, respectively, of a vertex position in space.
void AMFImporter::ParseNode_Coordinates()
{
CAMFImporter_NodeElement* ne;
// create new color object.
ne = new CAMFImporter_NodeElement_Coordinates(mNodeElement_Cur);
CAMFImporter_NodeElement_Coordinates& als = *((CAMFImporter_NodeElement_Coordinates*)ne);// alias for convenience
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool read_flag[3] = { false, false, false };
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("coordinates");
MACRO_NODECHECK_READCOMP_F("x", read_flag[0], als.Coordinate.x);
MACRO_NODECHECK_READCOMP_F("y", read_flag[1], als.Coordinate.y);
MACRO_NODECHECK_READCOMP_F("z", read_flag[2], als.Coordinate.z);
MACRO_NODECHECK_LOOPEND("coordinates");
ParseHelper_Node_Exit();
// check that all components was defined
if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all coordinate's components are defined.");
}// 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.
}
// <volume
// materialid="" - Which material to use.
// type="" - What this volume describes can be “region” or “support”. If none specified, “object” is assumed. If support, then the geometric
// requirements 1-8 listed in section 5 do not need to be maintained.
// >
// </volume>
// Defines a volume from the established vertex list.
// Multi elements - Yes.
// Parent element - <mesh>.
void AMFImporter::ParseNode_Volume()
{
std::string materialid;
std::string type;
CAMFImporter_NodeElement* ne;
// Read attributes for node <color>.
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECK_RET("materialid", materialid, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
MACRO_ATTRREAD_LOOPEND;
// create new object.
ne = new CAMFImporter_NodeElement_Volume(mNodeElement_Cur);
// and assign read data
((CAMFImporter_NodeElement_Volume*)ne)->MaterialID = materialid;
((CAMFImporter_NodeElement_Volume*)ne)->Type = type;
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool col_read = false;
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("volume");
if(XML_CheckNode_NameEqual("color"))
{
// Check if data already defined.
if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <volume>.");
// read data and set flag about it
ParseNode_Color();
col_read = true;
continue;
}
if(XML_CheckNode_NameEqual("triangle")) { ParseNode_Triangle(); continue; }
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
MACRO_NODECHECK_LOOPEND("volume");
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.
}
// <triangle>
// </triangle>
// Defines a 3D triangle from three vertices, according to the right-hand rule (counter-clockwise when looking from the outside).
// Multi elements - Yes.
// Parent element - <volume>.
//
// Children elements:
// <v1>, <v2>, <v3>
// Multi elements - No.
// Index of the desired vertices in a triangle or edge.
void AMFImporter::ParseNode_Triangle()
{
CAMFImporter_NodeElement* ne;
// create new color object.
ne = new CAMFImporter_NodeElement_Triangle(mNodeElement_Cur);
CAMFImporter_NodeElement_Triangle& als = *((CAMFImporter_NodeElement_Triangle*)ne);// alias for convenience
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool col_read = false, tex_read = false;
bool read_flag[3] = { false, false, false };
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("triangle");
if(XML_CheckNode_NameEqual("color"))
{
// Check if data already defined.
if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <triangle>.");
// read data and set flag about it
ParseNode_Color();
col_read = true;
continue;
}
if(XML_CheckNode_NameEqual("texmap"))// new name of node: "texmap".
{
// Check if data already defined.
if(tex_read) Throw_MoreThanOnceDefined("texmap", "Only one texture coordinate can be defined for <triangle>.");
// read data and set flag about it
ParseNode_TexMap();
tex_read = true;
continue;
}
else if(XML_CheckNode_NameEqual("map"))// old name of node: "map".
{
// Check if data already defined.
if(tex_read) Throw_MoreThanOnceDefined("map", "Only one texture coordinate can be defined for <triangle>.");
// read data and set flag about it
ParseNode_TexMap(true);
tex_read = true;
continue;
}
MACRO_NODECHECK_READCOMP_U32("v1", read_flag[0], als.V[0]);
MACRO_NODECHECK_READCOMP_U32("v2", read_flag[1], als.V[1]);
MACRO_NODECHECK_READCOMP_U32("v3", read_flag[2], als.V[2]);
MACRO_NODECHECK_LOOPEND("triangle");
ParseHelper_Node_Exit();
// check that all components was defined
if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all vertices of the triangle are defined.");
}// 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.
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

View File

@ -0,0 +1,122 @@
/// \file AMFImporter_Macro.hpp
/// \brief Useful macrodefines.
/// \date 2016
/// \author smal.root@gmail.com
#ifndef AMFIMPORTER_MACRO_HPP_INCLUDED
#define AMFIMPORTER_MACRO_HPP_INCLUDED
/// \def MACRO_ATTRREAD_LOOPBEG
/// Begin of loop that read attributes values.
#define MACRO_ATTRREAD_LOOPBEG \
for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
{ \
std::string an(mReader->getAttributeName(idx));
/// \def MACRO_ATTRREAD_LOOPEND
/// End of loop that read attributes values.
#define MACRO_ATTRREAD_LOOPEND \
Throw_IncorrectAttr(an); \
}
/// \def MACRO_ATTRREAD_LOOPEND_WSKIP
/// End of loop that read attributes values. Difference from \ref MACRO_ATTRREAD_LOOPEND in that: current macro skip unknown attributes, but
/// \ref MACRO_ATTRREAD_LOOPEND throw an exception.
#define MACRO_ATTRREAD_LOOPEND_WSKIP \
continue; \
}
/// \def MACRO_ATTRREAD_CHECK_REF
/// Check curent attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
/// "continue" will called.
/// \param [in] pAttrName - attribute name.
/// \param [out] pVarName - output variable name.
/// \param [in] pFunction - function which read attribute value and write it to pVarName.
#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
if(an == pAttrName) \
{ \
pFunction(idx, pVarName); \
continue; \
}
/// \def MACRO_ATTRREAD_CHECK_RET
/// Check curent attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
/// If result was read then "continue" will called.
/// \param [in] pAttrName - attribute name.
/// \param [out] pVarName - output variable name.
/// \param [in] pFunction - function which read attribute value and write it to pVarName.
#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
if(an == pAttrName) \
{ \
pVarName = pFunction(idx); \
continue; \
}
/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
/// Begin of loop of parsing child nodes. Do not add ';' at end.
/// \param [in] pNodeName - current node name.
#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
do { \
bool close_found = false; \
\
while(mReader->read()) \
{ \
if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
{
/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
/// End of loop of parsing child nodes.
/// \param [in] pNodeName - current node name.
#define MACRO_NODECHECK_LOOPEND(pNodeName) \
XML_CheckNode_SkipUnsupported(pNodeName); \
}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
{ \
if(XML_CheckNode_NameEqual(pNodeName)) \
{ \
close_found = true; \
\
break; \
} \
}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
}/* while(mReader->read()) */ \
\
if(!close_found) Throw_CloseNotFound(pNodeName); \
\
} while(false)
/// \def MACRO_NODECHECK_READCOMP_F
/// Check curent node name and if it equal to requested then read value. Result write to output variable of type "float".
/// If result was read then "continue" will called. Also check if node data already read then raise exception.
/// \param [in] pNodeName - node name.
/// \param [in, out] pReadFlag - read flag.
/// \param [out] pVarName - output variable name.
#define MACRO_NODECHECK_READCOMP_F(pNodeName, pReadFlag, pVarName) \
if(XML_CheckNode_NameEqual(pNodeName)) \
{ \
/* Check if field already read before. */ \
if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
/* Read color component and assign it to object. */ \
pVarName = XML_ReadNode_GetVal_AsFloat(); \
pReadFlag = true; \
continue; \
}
/// \def MACRO_NODECHECK_READCOMP_U32
/// Check curent node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
/// If result was read then "continue" will called. Also check if node data already read then raise exception.
/// \param [in] pNodeName - node name.
/// \param [in, out] pReadFlag - read flag.
/// \param [out] pVarName - output variable name.
#define MACRO_NODECHECK_READCOMP_U32(pNodeName, pReadFlag, pVarName) \
if(XML_CheckNode_NameEqual(pNodeName)) \
{ \
/* Check if field already read before. */ \
if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
/* Read color component and assign it to object. */ \
pVarName = XML_ReadNode_GetVal_AsU32(); \
pReadFlag = true; \
continue; \
}
#endif // AMFIMPORTER_MACRO_HPP_INCLUDED

View File

@ -0,0 +1,268 @@
/// \file AMFImporter_Material.cpp
/// \brief Parsing data from material nodes.
/// \date 2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
#include "AMFImporter.hpp"
#include "AMFImporter_Macro.hpp"
namespace Assimp
{
// <color
// profile="" - The ICC color space used to interpret the three color channels <r>, <g> and <b>.
// >
// </color>
// A color definition.
// Multi elements - No.
// Parent element - <material>, <object>, <volume>, <vertex>, <triangle>.
//
// "profile" can be one of "sRGB", "AdobeRGB", "Wide-Gamut-RGB", "CIERGB", "CIELAB", or "CIEXYZ".
// Children elements:
// <r>, <g>, <b>, <a>
// Multi elements - No.
// Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The
// values can be specified as constants, or as a formula depending on the coordinates.
void AMFImporter::ParseNode_Color()
{
std::string profile;
CAMFImporter_NodeElement* ne;
// Read attributes for node <color>.
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECK_RET("profile", profile, mReader->getAttributeValue);
MACRO_ATTRREAD_LOOPEND;
// create new color object.
ne = new CAMFImporter_NodeElement_Color(mNodeElement_Cur);
CAMFImporter_NodeElement_Color& als = *((CAMFImporter_NodeElement_Color*)ne);// alias for convenience
als.Profile = profile;
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool read_flag[4] = { false, false, false, false };
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("color");
MACRO_NODECHECK_READCOMP_F("r", read_flag[0], als.Color.r);
MACRO_NODECHECK_READCOMP_F("g", read_flag[1], als.Color.g);
MACRO_NODECHECK_READCOMP_F("b", read_flag[2], als.Color.b);
MACRO_NODECHECK_READCOMP_F("a", read_flag[3], als.Color.a);
MACRO_NODECHECK_LOOPEND("color");
ParseHelper_Node_Exit();
// check that all components was defined
if(!(read_flag[0] && read_flag[1] && read_flag[2])) throw DeadlyImportError("Not all color components are defined.");
// check if <a> is absent. Then manualy add "a == 1".
if(!read_flag[3]) als.Color.a = 1;
}// if(!mReader->isEmptyElement())
else
{
mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
}// if(!mReader->isEmptyElement()) else
als.Composed = false;
mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
}
// <material
// id="" - A unique material id. material ID "0" is reserved to denote no material (void) or sacrificial material.
// >
// </material>
// An available material.
// Multi elements - Yes.
// Parent element - <amf>.
void AMFImporter::ParseNode_Material()
{
std::string id;
CAMFImporter_NodeElement* ne;
// Read attributes for node <color>.
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
MACRO_ATTRREAD_LOOPEND;
// create new object.
ne = new CAMFImporter_NodeElement_Material(mNodeElement_Cur);
// and assign read data
((CAMFImporter_NodeElement_Material*)ne)->ID = id;
// Check for child nodes
if(!mReader->isEmptyElement())
{
bool col_read = false;
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("material");
if(XML_CheckNode_NameEqual("color"))
{
// Check if data already defined.
if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <material>.");
// read data and set flag about it
ParseNode_Color();
col_read = true;
continue;
}
if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
MACRO_NODECHECK_LOOPEND("material");
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.
}
// <texture
// id="" - Assigns a unique texture id for the new texture.
// width="" - Width (horizontal size, x) of the texture, in pixels.
// height="" - Height (lateral size, y) of the texture, in pixels.
// depth="" - Depth (vertical size, z) of the texture, in pixels.
// type="" - Encoding of the data in the texture. Currently allowed values are "grayscale" only. In grayscale mode, each pixel is represented by one byte
// in the range of 0-255. When the texture is referenced using the tex function, these values are converted into a single floating point number in the
// range of 0-1 (see Annex 2). A full color graphics will typically require three textures, one for each of the color channels. A graphic involving
// transparency may require a fourth channel.
// tiled="" - If true then texture repeated when UV-coordinates is greater than 1.
// >
// </triangle>
// Specifies an texture data to be used as a map. Lists a sequence of Base64 values specifying values for pixels from left to right then top to bottom,
// then layer by layer.
// Multi elements - Yes.
// Parent element - <amf>.
void AMFImporter::ParseNode_Texture()
{
std::string id;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1;
std::string type;
bool tiled = false;
std::string enc64_data;
CAMFImporter_NodeElement* ne;
// Read attributes for node <color>.
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("width", width, XML_ReadNode_GetAttrVal_AsU32);
MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsU32);
MACRO_ATTRREAD_CHECK_RET("depth", depth, XML_ReadNode_GetAttrVal_AsU32);
MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("tiled", tiled, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// create new texture object.
ne = new CAMFImporter_NodeElement_Texture(mNodeElement_Cur);
CAMFImporter_NodeElement_Texture& als = *((CAMFImporter_NodeElement_Texture*)ne);// alias for convenience
// Check for child nodes
if(!mReader->isEmptyElement()) XML_ReadNode_GetVal_AsString(enc64_data);
// check that all components was defined
if(id.empty()) throw DeadlyImportError("ID for texture must be defined.");
if(width < 1) Throw_IncorrectAttrValue("width");
if(height < 1) Throw_IncorrectAttrValue("height");
if(depth < 1) Throw_IncorrectAttrValue("depth");
if(type != "grayscale") Throw_IncorrectAttrValue("type");
if(enc64_data.empty()) throw DeadlyImportError("Texture data not defined.");
// copy data
als.ID = id;
als.Width = width;
als.Height = height;
als.Depth = depth;
als.Tiled = tiled;
ParseHelper_Decode_Base64(enc64_data, als.Data);
// check data size
if((width * height * depth) != als.Data.size()) throw DeadlyImportError("Texture has incorrect data size.");
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.
}
// <texmap
// rtexid="" - Texture ID for red color component.
// gtexid="" - Texture ID for green color component.
// btexid="" - Texture ID for blue color component.
// atexid="" - Texture ID for alpha color component. Optional.
// >
// </texmap>, old name: <map>
// Specifies texture coordinates for triangle.
// Multi elements - No.
// Parent element - <triangle>.
// Children elements:
// <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>. Old name: <u1>, <u2>, <u3>, <v1>, <v2>, <v3>.
// Multi elements - No.
// Texture coordinates for every vertex of triangle.
void AMFImporter::ParseNode_TexMap(const bool pUseOldName)
{
std::string rtexid, gtexid, btexid, atexid;
CAMFImporter_NodeElement* ne;
// Read attributes for node <color>.
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECK_RET("rtexid", rtexid, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("gtexid", gtexid, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("btexid", btexid, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("atexid", atexid, mReader->getAttributeValue);
MACRO_ATTRREAD_LOOPEND;
// create new texture coordinates object.
ne = new CAMFImporter_NodeElement_TexMap(mNodeElement_Cur);
CAMFImporter_NodeElement_TexMap& als = *((CAMFImporter_NodeElement_TexMap*)ne);// alias for convenience
// check data
if(rtexid.empty() && gtexid.empty() && btexid.empty()) throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined.");
// Check for children nodes
XML_CheckNode_MustHaveChildren();
// read children nodes
bool read_flag[6] = { false, false, false, false, false, false };
ParseHelper_Node_Enter(ne);
if(!pUseOldName)
{
MACRO_NODECHECK_LOOPBEGIN("texmap");
MACRO_NODECHECK_READCOMP_F("utex1", read_flag[0], als.TextureCoordinate[0].x);
MACRO_NODECHECK_READCOMP_F("utex2", read_flag[1], als.TextureCoordinate[1].x);
MACRO_NODECHECK_READCOMP_F("utex3", read_flag[2], als.TextureCoordinate[2].x);
MACRO_NODECHECK_READCOMP_F("vtex1", read_flag[3], als.TextureCoordinate[0].y);
MACRO_NODECHECK_READCOMP_F("vtex2", read_flag[4], als.TextureCoordinate[1].y);
MACRO_NODECHECK_READCOMP_F("vtex3", read_flag[5], als.TextureCoordinate[2].y);
MACRO_NODECHECK_LOOPEND("texmap");
}
else
{
MACRO_NODECHECK_LOOPBEGIN("map");
MACRO_NODECHECK_READCOMP_F("u1", read_flag[0], als.TextureCoordinate[0].x);
MACRO_NODECHECK_READCOMP_F("u2", read_flag[1], als.TextureCoordinate[1].x);
MACRO_NODECHECK_READCOMP_F("u3", read_flag[2], als.TextureCoordinate[2].x);
MACRO_NODECHECK_READCOMP_F("v1", read_flag[3], als.TextureCoordinate[0].y);
MACRO_NODECHECK_READCOMP_F("v2", read_flag[4], als.TextureCoordinate[1].y);
MACRO_NODECHECK_READCOMP_F("v3", read_flag[5], als.TextureCoordinate[2].y);
MACRO_NODECHECK_LOOPEND("map");
}// if(!pUseOldName) else
ParseHelper_Node_Exit();
// check that all components was defined
if(!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5]))
throw DeadlyImportError("Not all texture coordinates are defined.");
// copy attributes data
als.TextureID_R = rtexid;
als.TextureID_G = gtexid;
als.TextureID_B = btexid;
als.TextureID_A = atexid;
mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

View File

@ -0,0 +1,381 @@
/// \file AMFImporter_Node.hpp
/// \brief Elements of scene graph.
/// \date 2016
/// \author smal.root@gmail.com
#ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H
#define INCLUDED_AI_AMF_IMPORTER_NODE_H
// Header files, stdlib.
#include <list>
#include <string>
#include <vector>
// Header files, Assimp.
#include "assimp/types.h"
#include "assimp/scene.h"
/// \class CAMFImporter_NodeElement
/// Base class for elements of nodes.
class CAMFImporter_NodeElement
{
/***********************************************/
/******************** Types ********************/
/***********************************************/
public:
/// \enum EType
/// Define what data type contain node element.
enum EType
{
ENET_Color, ///< Color element: <color>.
ENET_Constellation,///< Grouping element: <constellation>.
ENET_Coordinates, ///< Coordinates element: <coordinates>.
ENET_Edge, ///< Edge element: <edge>.
ENET_Instance, ///< Grouping element: <constellation>.
ENET_Material, ///< Material element: <material>.
ENET_Metadata, ///< Metadata element: <metadata>.
ENET_Mesh, ///< Metadata element: <mesh>.
ENET_Object, ///< Element which hold object: <object>.
ENET_Root, ///< Root element: <amf>.
ENET_Triangle, ///< Triangle element: <triangle>.
ENET_TexMap, ///< Texture coordinates element: <texmap> or <map>.
ENET_Texture, ///< Texture element: <texture>.
ENET_Vertex, ///< Vertex element: <vertex>.
ENET_Vertices, ///< Vertex element: <vertices>.
ENET_Volume, ///< Volume element: <volume>.
ENET_Invalid ///< Element has invalid type and possible contain invalid data.
};
/***********************************************/
/****************** Constants ******************/
/***********************************************/
public:
const EType Type;///< Type of element.
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
std::string ID;///< ID of element.
CAMFImporter_NodeElement* Parent;///< Parrent element. If nullptr then this node is root.
std::list<CAMFImporter_NodeElement*> Child;///< Child elements.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CAMFImporter_NodeElement(const CAMFImporter_NodeElement& pNodeElement)
/// Disabled copy constructor.
CAMFImporter_NodeElement(const CAMFImporter_NodeElement& pNodeElement);
/// \fn CAMFImporter_NodeElement& operator=(const CAMFImporter_NodeElement& pNodeElement)
/// Disabled assign operator.
CAMFImporter_NodeElement& operator=(const CAMFImporter_NodeElement& pNodeElement);
/// \fn CAMFImporter_NodeElement()
/// Disabled default constructor.
CAMFImporter_NodeElement();
protected:
/// \fn CAMFImporter_NodeElement(const EType pType, CAMFImporter_NodeElement* pParent)
/// In constructor inheritor must set element type.
/// \param [in] pType - element type.
/// \param [in] pParent - parent element.
CAMFImporter_NodeElement(const EType pType, CAMFImporter_NodeElement* pParent)
: Type(pType), Parent(pParent)
{}
};// class IAMFImporter_NodeElement
/// \struct CAMFImporter_NodeElement_Constellation
/// A collection of objects or constellations with specific relative locations.
struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Constellation, pParent)
{}
};// struct CAMFImporter_NodeElement_Constellation
/// \struct CAMFImporter_NodeElement_Instance
/// Part of constellation.
struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
std::string ObjectID;///< ID of object for instanciation.
/// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to
/// create an instance of the object in the current constellation.
aiVector3D Delta;
/// \var Rotation - The rotation, in degrees, to rotate the referenced object about its x, y, and z axes, respectively, to create an
/// instance of the object in the current constellation. Rotations shall be executed in order of x first, then y, then z.
aiVector3D Rotation;
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Instance, pParent)
{}
};// struct CAMFImporter_NodeElement_Instance
/// \struct CAMFImporter_NodeElement_Metadata
/// Structure that define metadata node.
struct CAMFImporter_NodeElement_Metadata : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
std::string Type;///< Type of "Value".
std::string Value;///< Value.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Metadata, pParent)
{}
};// struct CAMFImporter_NodeElement_Metadata
/// \struct CAMFImporter_NodeElement_Root
/// Structure that define root node.
struct CAMFImporter_NodeElement_Root : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
std::string Unit;///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
std::string Version;///< Version of format.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Root, pParent)
{}
};// struct CAMFImporter_NodeElement_Root
/// \struct CAMFImporter_NodeElement_Color
/// Structure that define object node.
struct CAMFImporter_NodeElement_Color : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
bool Composed;///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
std::string Color_Composed[4];///< By components formulas of composed color. [0..3] => RGBA.
aiColor4D Color;///< Constant color.
std::string Profile;///< The ICC color space used to interpret the three color channels <r>, <g> and <b>..
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Color, pParent)
{}
};// struct CAMFImporter_NodeElement_Color
/// \struct CAMFImporter_NodeElement_Material
/// Structure that define material node.
struct CAMFImporter_NodeElement_Material : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Material, pParent)
{}
};// struct CAMFImporter_NodeElement_Material
/// \struct CAMFImporter_NodeElement_Object
/// Structure that define object node.
struct CAMFImporter_NodeElement_Object : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Object, pParent)
{}
};// struct CAMFImporter_NodeElement_Object
/// \struct CAMFImporter_NodeElement_Mesh
/// Structure that define mesh node.
struct CAMFImporter_NodeElement_Mesh : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Mesh, pParent)
{}
};// struct CAMFImporter_NodeElement_Mesh
/// \struct CAMFImporter_NodeElement_Vertex
/// Structure that define vertex node.
struct CAMFImporter_NodeElement_Vertex : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Vertex, pParent)
{}
};// struct CAMFImporter_NodeElement_Vertex
/// \struct CAMFImporter_NodeElement_Edge
/// Structure that define edge node.
struct CAMFImporter_NodeElement_Edge : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Edge, pParent)
{}
};// struct CAMFImporter_NodeElement_Vertex
/// \struct CAMFImporter_NodeElement_Vertices
/// Structure that define vertices node.
struct CAMFImporter_NodeElement_Vertices : public CAMFImporter_NodeElement
{
/// \fn CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Vertices, pParent)
{}
};// struct CAMFImporter_NodeElement_Vertices
/// \struct CAMFImporter_NodeElement_Volume
/// Structure that define volume node.
struct CAMFImporter_NodeElement_Volume : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
std::string MaterialID;///< Which material to use.
std::string Type;///< What this volume describes can be “region” or “support”. If none specified, “object” is assumed.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Volume, pParent)
{}
};// struct CAMFImporter_NodeElement_Volume
/// \struct CAMFImporter_NodeElement_Coordinates
/// Structure that define coordinates node.
struct CAMFImporter_NodeElement_Coordinates : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
aiVector3D Coordinate;///< Coordinate.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Coordinates, pParent)
{}
};// struct CAMFImporter_NodeElement_Coordinates
/// \struct CAMFImporter_NodeElement_TexMap
/// Structure that define texture coordinates node.
struct CAMFImporter_NodeElement_TexMap : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
aiVector3D TextureCoordinate[3];///< Texture coordinates.
std::string TextureID_R;///< Texture ID for red color component.
std::string TextureID_G;///< Texture ID for green color component.
std::string TextureID_B;///< Texture ID for blue color component.
std::string TextureID_A;///< Texture ID for alpha color component.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_TexMap, pParent)
{}
};// struct CAMFImporter_NodeElement_TexMap
/// \struct CAMFImporter_NodeElement_Triangle
/// Structure that define triangle node.
struct CAMFImporter_NodeElement_Triangle : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
size_t V[3];///< Triangle vertices.
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Triangle, pParent)
{}
};// struct CAMFImporter_NodeElement_Triangle
/// \struct CAMFImporter_NodeElement_Texture
/// Structure that define texture node.
struct CAMFImporter_NodeElement_Texture : public CAMFImporter_NodeElement
{
/****************** Variables ******************/
size_t Width, Height, Depth;///< Size of the texture.
std::vector<uint8_t> Data;///< Data of the texture.
bool Tiled;
/****************** Functions ******************/
/// \fn CAMFImporter_NodeElement_Texture(CAMFImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
CAMFImporter_NodeElement_Texture(CAMFImporter_NodeElement* pParent)
: CAMFImporter_NodeElement(ENET_Texture, pParent)
{}
};// struct CAMFImporter_NodeElement_Texture
#endif // INCLUDED_AI_AMF_IMPORTER_NODE_H

View File

@ -0,0 +1,928 @@
/// \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 "SceneCombiner.h"
#include "StandardShapes.h"
// Header files, stdlib.
#include <algorithm>
#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.size() != 0)
{
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(uint8_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()) { tex_size += src_texture[0]->Data.size(); step++, off_g++, off_b++; }
if(!pID_G.empty()) { tex_size += src_texture[1]->Data.size(); step++, off_b++; }
if(!pID_B.empty()) { tex_size += src_texture[2]->Data.size(); step++; }
if(!pID_A.empty()) { 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++)
converted_texture.Data[idx_target] = src_texture[pSrcTexNum]->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.size() == 0) 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.size() > 0) pOutputList_Separated.push_back(face_list_cur);
} while(pInputList.size() > 0);
}
void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const
{
if(pMetadataList.size() > 0)
{
if(pSceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
// copy collected metadata to output node.
pSceneNode.mMetaData = new aiMetadata();
pSceneNode.mMetaData->mNumProperties = pMetadataList.size();
pSceneNode.mMetaData->mKeys = new aiString[pSceneNode.mMetaData->mNumProperties];
pSceneNode.mMetaData->mValues = new aiMetadataEntry[pSceneNode.mMetaData->mNumProperties];
size_t meta_idx = 0;
for(const CAMFImporter_NodeElement_Metadata& metadata: pMetadataList)
{
pSceneNode.mMetaData->Set(meta_idx++, metadata.Type, metadata.Value.c_str());
}
}// if(pMetadataList.size() > 0)
}
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] = tri_al.V[0];
complex_face.Face.mIndices[1] = tri_al.V[1];
complex_face.Face.mIndices[2] = 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;
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] = 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 = 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 transfered,
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] = 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 = 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] = 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 = vert_arr.size();
tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
tmesh->mFaces = new aiFace[face_list_cur.size()];
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(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.size() > 0)
{
std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
pSceneNode.mNumMeshes = 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 thru 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 apllying 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.size() == 0) throw DeadlyImportError("<constellation> must have at least one <instance>.");
size_t ch_idx = 0;
con_node->mNumChildren = 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 thru 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 thru 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.size() > 0)
{
std::list<aiNode*>::const_iterator nl_it = node_list.begin();
pScene->mRootNode->mNumChildren = 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.size() > 0)
{
std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
pScene->mNumMeshes = 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 = 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 = tex_convd.Width;
pScene->mTextures[idx]->mHeight = 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 = 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 + std::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 thru children of root and collect data
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

View File

@ -202,6 +202,16 @@ SET(ASSIMP_LOADER_SRCS "")
SET(ASSIMP_IMPORTERS_ENABLED "") # list of enabled importers
SET(ASSIMP_IMPORTERS_DISABLED "") # disabled list (used to print)
ADD_ASSIMP_IMPORTER( AMF
AMFImporter.hpp
AMFImporter_Macro.hpp
AMFImporter_Node.hpp
AMFImporter.cpp
AMFImporter_Geometry.cpp
AMFImporter_Material.cpp
AMFImporter_Postprocess.cpp
)
ADD_ASSIMP_IMPORTER( 3DS
3DSConverter.cpp
3DSHelper.h
@ -600,6 +610,23 @@ ADD_ASSIMP_IMPORTER( X
XFileExporter.cpp
)
ADD_ASSIMP_IMPORTER(X3D
X3DImporter.cpp
X3DImporter.hpp
X3DImporter_Geometry2D.cpp
X3DImporter_Geometry3D.cpp
X3DImporter_Group.cpp
X3DImporter_Light.cpp
X3DImporter_Macro.hpp
X3DImporter_Metadata.cpp
X3DImporter_Networking.cpp
X3DImporter_Node.hpp
X3DImporter_Postprocess.cpp
X3DImporter_Rendering.cpp
X3DImporter_Shape.cpp
X3DImporter_Texturing.cpp
)
ADD_ASSIMP_IMPORTER( GLTF
glTFAsset.h
glTFAsset.inl

View File

@ -117,12 +117,12 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
return;
}
vertices.reserve(tempFaces.size());
faces.reserve(tempFaces.size() / 3);
m_vertices.reserve(tempFaces.size());
m_faces.reserve(tempFaces.size() / 3);
mapping_offsets.resize(tempVerts.size());
mapping_counts.resize(tempVerts.size(),0);
mappings.resize(tempFaces.size());
m_mapping_offsets.resize(tempVerts.size());
m_mapping_counts.resize(tempVerts.size(),0);
m_mappings.resize(tempFaces.size());
const size_t vertex_count = tempVerts.size();
@ -135,29 +135,29 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
DOMError("polygon vertex index out of range",&PolygonVertexIndex);
}
vertices.push_back(tempVerts[absi]);
m_vertices.push_back(tempVerts[absi]);
++count;
++mapping_counts[absi];
++m_mapping_counts[absi];
if (index < 0) {
faces.push_back(count);
m_faces.push_back(count);
count = 0;
}
}
unsigned int cursor = 0;
for (size_t i = 0, e = tempVerts.size(); i < e; ++i) {
mapping_offsets[i] = cursor;
cursor += mapping_counts[i];
m_mapping_offsets[i] = cursor;
cursor += m_mapping_counts[i];
mapping_counts[i] = 0;
m_mapping_counts[i] = 0;
}
cursor = 0;
for(int index : tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor++;
m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++;
}
// if settings.readAllLayers is true:
@ -191,84 +191,84 @@ MeshGeometry::~MeshGeometry()
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
return vertices;
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetNormals() const {
return normals;
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetTangents() const {
return tangents;
return m_tangents;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const {
return binormals;
return m_binormals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const {
return faces;
return m_faces;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const {
static const std::vector<aiVector2D> empty;
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : uvs[ index ];
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ];
}
std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const {
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : uvNames[ index ];
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ];
}
const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const {
static const std::vector<aiColor4D> empty;
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : colors[ index ];
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ];
}
const MatIndexArray& MeshGeometry::GetMaterialIndices() const {
return materials;
return m_materials;
}
// ------------------------------------------------------------------------------------------------
const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const {
if ( in_index >= mapping_counts.size() ) {
if ( in_index >= m_mapping_counts.size() ) {
return NULL;
}
ai_assert( mapping_counts.size() == mapping_offsets.size() );
count = mapping_counts[ in_index ];
ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
count = m_mapping_counts[ in_index ];
ai_assert( count != 0 );
ai_assert( mapping_offsets[ in_index ] + count <= mappings.size() );
// ai_assert( count != 0 );
ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
return &mappings[ mapping_offsets[ in_index ] ];
return &m_mappings[ m_mapping_offsets[ in_index ] ];
}
// ------------------------------------------------------------------------------------------------
unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const {
ai_assert( in_index < vertices.size() );
ai_assert( in_index < m_vertices.size() );
// in the current conversion pattern this will only be needed if
// weights are present, so no need to always pre-compute this table
if ( facesVertexStartIndices.empty() ) {
facesVertexStartIndices.resize( faces.size() + 1, 0 );
if ( m_facesVertexStartIndices.empty() ) {
m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 );
std::partial_sum( faces.begin(), faces.end(), facesVertexStartIndices.begin() + 1 );
facesVertexStartIndices.pop_back();
std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 );
m_facesVertexStartIndices.pop_back();
}
ai_assert( facesVertexStartIndices.size() == faces.size() );
ai_assert( m_facesVertexStartIndices.size() == m_faces.size() );
const std::vector<unsigned int>::iterator it = std::upper_bound(
facesVertexStartIndices.begin(),
facesVertexStartIndices.end(),
m_facesVertexStartIndices.begin(),
m_facesVertexStartIndices.end(),
in_index
);
return static_cast< unsigned int >( std::distance( facesVertexStartIndices.begin(), it - 1 ) );
return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) );
}
// ------------------------------------------------------------------------------------------------
@ -327,18 +327,18 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
}
const Element* Name = source["Name"];
uvNames[index] = "";
m_uvNames[index] = "";
if(Name) {
uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
}
ReadVertexDataUV(uvs[index],source,
ReadVertexDataUV(m_uvs[index],source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementMaterial") {
if (materials.size() > 0) {
if (m_materials.size() > 0) {
FBXImporter::LogError("ignoring additional material layer");
return;
}
@ -362,37 +362,37 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
return;
}
std::swap(temp_materials, materials);
std::swap(temp_materials, m_materials);
}
else if (type == "LayerElementNormal") {
if (normals.size() > 0) {
if (m_normals.size() > 0) {
FBXImporter::LogError("ignoring additional normal layer");
return;
}
ReadVertexDataNormals(normals,source,
ReadVertexDataNormals(m_normals,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementTangent") {
if (tangents.size() > 0) {
if (m_tangents.size() > 0) {
FBXImporter::LogError("ignoring additional tangent layer");
return;
}
ReadVertexDataTangents(tangents,source,
ReadVertexDataTangents(m_tangents,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementBinormal") {
if (binormals.size() > 0) {
if (m_binormals.size() > 0) {
FBXImporter::LogError("ignoring additional binormal layer");
return;
}
ReadVertexDataBinormals(binormals,source,
ReadVertexDataBinormals(m_binormals,source,
MappingInformationType,
ReferenceInformationType
);
@ -404,7 +404,7 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
return;
}
ReadVertexDataColors(colors[index],source,
ReadVertexDataColors(m_colors[index],source,
MappingInformationType,
ReferenceInformationType
);
@ -515,10 +515,10 @@ void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, c
ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
"Normals",
"NormalsIndex",
vertices.size(),
mapping_counts,
mapping_offsets,
mappings);
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
@ -530,10 +530,10 @@ void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope
ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
"UV",
"UVIndex",
vertices.size(),
mapping_counts,
mapping_offsets,
mappings);
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
@ -545,10 +545,10 @@ void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, cons
ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
"Colors",
"ColorIndex",
vertices.size(),
mapping_counts,
mapping_offsets,
mappings);
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
@ -564,10 +564,10 @@ void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out,
ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
vertices.size(),
mapping_counts,
mapping_offsets,
mappings);
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
@ -583,10 +583,10 @@ void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_ou
ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
vertices.size(),
mapping_counts,
mapping_offsets,
mappings);
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
@ -595,7 +595,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const size_t face_count = faces.size();
const size_t face_count = m_faces.size();
ai_assert(face_count);
// materials are handled separately. First of all, they are assigned per-face
@ -614,10 +614,10 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
materials_out.clear();
}
materials.assign(vertices.size(),materials_out[0]);
m_materials.assign(m_vertices.size(),materials_out[0]);
}
else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
materials.resize(face_count);
m_materials.resize(face_count);
if(materials_out.size() != face_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")

View File

@ -156,21 +156,21 @@ private:
private:
// cached data arrays
MatIndexArray materials;
std::vector<aiVector3D> vertices;
std::vector<unsigned int> faces;
mutable std::vector<unsigned int> facesVertexStartIndices;
std::vector<aiVector3D> tangents;
std::vector<aiVector3D> binormals;
std::vector<aiVector3D> normals;
MatIndexArray m_materials;
std::vector<aiVector3D> m_vertices;
std::vector<unsigned int> m_faces;
mutable std::vector<unsigned int> m_facesVertexStartIndices;
std::vector<aiVector3D> m_tangents;
std::vector<aiVector3D> m_binormals;
std::vector<aiVector3D> m_normals;
std::string uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiVector2D> uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiColor4D> colors[ AI_MAX_NUMBER_OF_COLOR_SETS ];
std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiVector2D> m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiColor4D> m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ];
std::vector<unsigned int> mapping_counts;
std::vector<unsigned int> mapping_offsets;
std::vector<unsigned int> mappings;
std::vector<unsigned int> m_mapping_counts;
std::vector<unsigned int> m_mapping_offsets;
std::vector<unsigned int> m_mappings;
};
}

View File

@ -1,4 +1,4 @@
/*
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
@ -53,6 +53,9 @@ corresponding preprocessor flag to selectively disable formats.
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
# include "XFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
# include "AMFImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
# include "3DSLoader.h"
#endif
@ -182,6 +185,9 @@ corresponding preprocessor flag to selectively disable formats.
#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
# include "D3MFImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
# include "X3DImporter.hpp"
#endif
namespace Assimp {
@ -199,6 +205,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
out.push_back( new ObjFileImporter());
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
out.push_back( new AMFImporter() );
#endif
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new Discreet3DSImporter());
#endif
@ -325,6 +334,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
out.push_back(new D3MFImporter() );
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
out.push_back( new X3DImporter() );
#endif
}
/** will delete all registered importers. */

View File

@ -50,6 +50,10 @@ OpenGEXExporter::OpenGEXExporter() {
OpenGEXExporter::~OpenGEXExporter() {
}
bool OpenGEXExporter::exportScene( const char *filename, const aiScene* pScene ) {
return true;
}
#endif // ASSIMP_BUILD_NO_OPENGEX_EXPORTER
} // Namespace OpenGEX

View File

@ -152,9 +152,8 @@ private:
VertexContainer();
~VertexContainer();
private:
VertexContainer( const VertexContainer & );
VertexContainer &operator = ( const VertexContainer & );
VertexContainer( const VertexContainer & ) = delete;
VertexContainer &operator = ( const VertexContainer & ) = delete;
};
struct RefInfo {
@ -170,9 +169,8 @@ private:
RefInfo( aiNode *node, Type type, std::vector<std::string> &names );
~RefInfo();
private:
RefInfo( const RefInfo & );
RefInfo &operator = ( const RefInfo & );
RefInfo( const RefInfo & ) = delete;
RefInfo &operator = ( const RefInfo & ) = delete;
};
struct ChildInfo {

View File

@ -113,7 +113,6 @@ struct LightObject {
bool shadowFlag;
};
struct CameraObject {
float focalLength;
float nearDepth;
@ -146,7 +145,6 @@ struct Name {
std::string name;
};
struct ObjectRef {
Object *targetStructure;
};
@ -173,7 +171,6 @@ struct BoneIndex {
unsigned short *arrayStorage;
};
struct BoneWeight {
int boneWeightCount;
const float *boneWeightArray;

View File

@ -121,8 +121,7 @@ void Subdivide(std::vector<aiVector3D>& positions)
aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
unsigned int numIndices)
{
if (positions.size() & numIndices || positions.empty() || !numIndices)
return NULL;
if (positions.empty() || !numIndices) return NULL;
// Determine which kinds of primitives the mesh consists of
aiMesh* out = new aiMesh();

View File

@ -38,6 +38,11 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file StdOStreamLogStream.h
* @brief Implementation of StdOStreamLogStream
*/
#ifndef AI_STROSTREAMLOGSTREAM_H_INC
#define AI_STROSTREAMLOGSTREAM_H_INC
@ -50,8 +55,7 @@ namespace Assimp {
/** @class StdOStreamLogStream
* @brief Logs into a std::ostream
*/
class StdOStreamLogStream : public LogStream
{
class StdOStreamLogStream : public LogStream {
public:
/** @brief Construction from an existing std::ostream
* @param _ostream Output stream to be used
@ -63,30 +67,33 @@ public:
/** @brief Writer */
void write(const char* message);
private:
std::ostream& ostream;
std::ostream& mOstream;
};
// ---------------------------------------------------------------------------
// Default constructor
inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream)
: ostream (_ostream)
{}
// ---------------------------------------------------------------------------
// Default constructor
inline StdOStreamLogStream::~StdOStreamLogStream()
{}
// ---------------------------------------------------------------------------
// Write method
inline void StdOStreamLogStream::write(const char* message)
{
ostream << message;
ostream.flush();
: mOstream (_ostream){
// empty
}
// ---------------------------------------------------------------------------
// Default constructor
inline StdOStreamLogStream::~StdOStreamLogStream() {
// empty
}
// ---------------------------------------------------------------------------
// Write method
inline void StdOStreamLogStream::write(const char* message) {
mOstream << message;
mOstream.flush();
}
// ---------------------------------------------------------------------------
} // Namespace Assimp
#endif // guard

View File

@ -1,4 +1,4 @@
/*
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
@ -408,7 +408,8 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
// the MSB flag is temporarily used by the extra verbose
// mode to tell us that the JoinVerticesProcess might have
// been executed already.
if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && abRefList[face.mIndices[a]])
if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
abRefList[face.mIndices[a]])
{
ReportError("aiMesh::mVertices[%i] is referenced twice - second "
"time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);

View File

@ -1,7 +1,51 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2016, 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 Win32DebugLogStream.h
* @brief Implementation of Win32DebugLogStream
*/
#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC
#define AI_WIN32DEBUGLOGSTREAM_H_INC
#ifdef WIN32
#ifdef _WIN32
#include <assimp/LogStream.hpp>
#include "windows.h"
@ -12,9 +56,7 @@ namespace Assimp {
/** @class Win32DebugLogStream
* @brief Logs into the debug stream from win32.
*/
class Win32DebugLogStream :
public LogStream
{
class Win32DebugLogStream : public LogStream {
public:
/** @brief Default constructor */
Win32DebugLogStream();
@ -27,24 +69,25 @@ public:
};
// ---------------------------------------------------------------------------
// Default constructor
inline Win32DebugLogStream::Win32DebugLogStream()
{}
inline
Win32DebugLogStream::Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
// Default constructor
inline Win32DebugLogStream::~Win32DebugLogStream()
{}
inline
Win32DebugLogStream::~Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
// Write method
inline void Win32DebugLogStream::write(const char* message)
{
OutputDebugStringA( message);
inline
void Win32DebugLogStream::write(const char* message) {
::OutputDebugStringA( message);
}
// ---------------------------------------------------------------------------
} // Namespace Assimp
#endif // ! WIN32
#endif // ! _WIN32
#endif // guard

1585
code/X3DImporter.cpp 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,951 @@
/// \file X3DImporter.hpp
/// \brief X3D-format files importer for Assimp.
/// \date 2015-2016
/// \author smal.root@gmail.com
// Thanks to acorn89 for support.
#ifndef INCLUDED_AI_X3D_IMPORTER_H
#define INCLUDED_AI_X3D_IMPORTER_H
#include "X3DImporter_Node.hpp"
// Header files, Assimp.
#include "assimp/DefaultLogger.hpp"
#include "assimp/importerdesc.h"
#include "assimp/ProgressHandler.hpp"
#include "assimp/types.h"
#include "BaseImporter.h"
#include "irrXMLWrapper.h"
namespace Assimp
{
/// \class X3DImporter
/// Class that holding scene graph which include: groups, geometry, metadata etc.
///
/// Limitations.
///
/// Pay attention that X3D is format for interactive graphic and simulations for web browsers.
/// So not all features can be imported using Assimp.
///
/// Unsupported nodes:
/// CAD geometry component:
/// "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet"
/// Core component:
/// "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo"
/// Distributed interactive simulation (DIS) component:
/// "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu"
/// Cube map environmental texturing component:
/// "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture"
/// Environmental effects component:
/// "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground"
/// Environmental sensor component:
/// "ProximitySensor", "TransformSensor", "VisibilitySensor"
/// Followers component:
/// "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser",
/// "PositionChaser2D", "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D"
/// Geospatial component:
/// "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
/// "GeoTouchSensor", "GeoTransform", "GeoViewpoint"
/// Humanoid Animation (H-Anim) component:
/// "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite"
/// Interpolation component:
/// "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
/// "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
/// "SplineScalarInterpolator", "SquadOrientationInterpolator",
/// Key device sensor component:
/// "KeySensor", "StringSensor"
/// Layering component:
/// "Layer", "LayerSet", "Viewport"
/// Layout component:
/// "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup"
/// Navigation component:
/// "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup"
/// Networking component:
/// "Anchor", "LoadSensor"
/// NURBS component:
/// "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
/// "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
/// "NurbsTrimmedSurface"
/// Particle systems component:
/// "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter",
/// "SurfaceEmitter", "VolumeEmitter", "WindPhysicsModel"
/// Picking component:
/// "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor"
/// Pointing device sensor component:
/// "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor"
/// Rendering component:
/// "ClipPlane"
/// Rigid body physics:
/// "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
/// "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint"
/// Scripting component:
/// "Script"
/// Programmable shaders component:
/// "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
/// "ShaderProgram",
/// Shape component:
/// "FillProperties", "LineProperties", "TwoSidedMaterial"
/// Sound component:
/// "AudioClip", "Sound"
/// Text component:
/// "FontStyle", "Text"
/// Texturing3D Component:
/// "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D",
/// "TextureTransform3D"
/// Texturing component:
/// "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator",
/// "TextureProperties",
/// Time component:
/// "TimeSensor"
/// Event Utilities component:
/// "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
/// Volume rendering component:
/// "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle",
/// "IsoSurfaceVolumeData", "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle",
/// "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", "VolumeData"
///
/// Supported nodes:
/// Core component:
/// "MetadataBoolean", "MetadataDouble", "MetadataFloat", "MetadataInteger", "MetadataSet", "MetadataString"
/// Geometry2D component:
/// "Arc2D", "ArcClose2D", "Circle2D", "Disk2D", "Polyline2D", "Polypoint2D", "Rectangle2D", "TriangleSet2D"
/// Geometry3D component:
/// "Box", "Cone", "Cylinder", "ElevationGrid", "Extrusion", "IndexedFaceSet", "Sphere"
/// Grouping component:
/// "Group", "StaticGroup", "Switch", "Transform"
/// Lighting component:
/// "DirectionalLight", "PointLight", "SpotLight"
/// Networking component:
/// "Inline"
/// Rendering component:
/// "Color", "ColorRGBA", "Coordinate", "IndexedLineSet", "IndexedTriangleFanSet", "IndexedTriangleSet", "IndexedTriangleStripSet", "LineSet",
/// "PointSet", "TriangleFanSet", "TriangleSet", "TriangleStripSet", "Normal"
/// Shape component:
/// "Shape", "Appearance", "Material"
/// Texturing component:
/// "ImageTexture", "TextureCoordinate", "TextureTransform"
///
/// Limitations of attribute "USE".
/// If "USE" is set then node must be empty, like that:
/// <Node USE='name'/>
/// not the
/// <Node USE='name'><!-- something --> </Node>
///
/// Ignored attributes: "creaseAngle", "convex", "solid".
///
/// Texture coordinates generating: only for Sphere, Cone, Cylinder. In all other case used PLANE mapping.
/// It's better that Assimp main code has powerfull texture coordinates generator. Then is not needed to
/// duplicate this code in every importer.
///
/// Lighting limitations.
/// If light source placed in some group with "DEF" set. And after that some node is use it group with "USE" attribute then
/// you will get error about duplicate light sources. That's happening because Assimp require names for lights but do not like
/// duplicates of it )).
///
/// Color for faces.
/// That's happening when attribute "colorPerVertex" is set to "false". But Assimp do not hold how many colors has mesh and require
/// equal length for mVertices and mColors. You will see the colors but vertices will use call which last used in "colorIdx".
///
/// That's all for now. Enjoy
///
class X3DImporter : public BaseImporter
{
/***********************************************/
/******************** Types ********************/
/***********************************************/
/***********************************************/
/****************** Constants ******************/
/***********************************************/
private:
static const aiImporterDesc Description;
/***********************************************/
/****************** Variables ******************/
/***********************************************/
private:
CX3DImporter_NodeElement* NodeElement_Cur;///< Current element.
irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
std::string mFileDir;
public:
std::list<CX3DImporter_NodeElement*> NodeElement_List;///< All elements of scene graph.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn X3DImporter(const X3DImporter& pScene)
/// Disabled copy constructor.
X3DImporter(const X3DImporter& pScene);
/// \fn X3DImporter& operator=(const X3DImporter& pScene)
/// Disabled assign operator.
X3DImporter& operator=(const X3DImporter& pScene);
/// \fn void Clear()
/// Clear all temporary data.
void Clear();
/***********************************************/
/************* Functions: find set *************/
/***********************************************/
/// \fn bool FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
/// Find requested node element. Search will be made in all existing nodes.
/// \param [in] pID - ID of requested element.
/// \param [in] pType - type of requested element.
/// \param [out] pElement - pointer to pointer to item found.
/// \return true - if the element is found, else - false.
bool FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement);
/// \fn bool FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
/// Find requested node element. Search will be made from pointed node down to childs.
/// \param [in] pStartNode - pointer to start node.
/// \param [in] pID - ID of requested element.
/// \param [in] pType - type of requested element.
/// \param [out] pElement - pointer to pointer to item found.
/// \return true - if the element is found, else - false.
bool FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID, const CX3DImporter_NodeElement::EType pType,
CX3DImporter_NodeElement** pElement);
/// \fn bool FindNodeElement(const std::string& pName, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
/// Find requested node element. For "Node"'s accounting flag "Static".
/// \param [in] pName - name of requested element.
/// \param [in] pType - type of requested element.
/// \param [out] pElement - pointer to pointer to item found.
/// \return true - if the element is found, else - false.
bool FindNodeElement(const std::string& pName, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement);
/***********************************************/
/********* Functions: postprocess set **********/
/***********************************************/
/// \fn aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const
/// \return transformation matrix from global coordinate system to local.
aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const;
/// \fn void PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
/// Check if child elements of node element is metadata and add it to temporary list.
/// \param [in] pNodeElement - node element where metadata is searching.
/// \param [out] pList - temporary list for collected metadata.
void PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const;
/// \fn bool bool PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const
/// Check if type of node element is metadata. E.g. <MetadataSet>, <MetadataString>.
/// \param [in] pType - checked type.
/// \return true - if the type corresponds to the metadata.
bool PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const;
/// \fn bool PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const
/// Check if type of node element is geometry object and can be used to build mesh. E.g. <Box>, <Arc2D>.
/// \param [in] pType - checked type.
/// \return true - if the type corresponds to the mesh.
bool PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const;
/// \fn void Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const
/// Read CX3DImporter_NodeElement_Light, create aiLight and add it to list of the lights.
/// \param [in] pNodeElement - reference to lisght element(<DirectionalLight>, <PointLight>, <SpotLight>).
/// \param [out] pSceneLightList - reference to list of the lights.
void Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const;
/// \fn void Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const
/// Create filled structure with type \ref aiMaterial from \ref CX3DImporter_NodeElement. This function itseld extract
/// all needed data from scene graph.
/// \param [in] pNodeElement - reference to material element(<Appearance>).
/// \param [out] pMaterial - pointer to pointer to created material. *pMaterial must be nullptr.
void Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const;
/// \fn void Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const
/// Create filled structure with type \ref aiMaterial from \ref CX3DImporter_NodeElement. This function itseld extract
/// all needed data from scene graph.
/// \param [in] pNodeElement - reference to geometry object.
/// \param [out] pMesh - pointer to pointer to created mesh. *pMesh must be nullptr.
void Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const;
/// \fn void Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const
/// Create aiNode from CX3DImporter_NodeElement. Also function check children and make recursive call.
/// \param [out] pNode - pointer to pointer to created node. *pNode must be nullptr.
/// \param [in] pNodeElement - CX3DImporter_NodeElement which read.
/// \param [out] pSceneNode - aiNode for filling.
/// \param [out] pSceneMeshList - list with aiMesh which belong to scene.
/// \param [out] pSceneMaterialList - list with aiMaterial which belong to scene.
/// \param [out] pSceneLightList - list with aiLight which belong to scene.
void Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const;
/// \fn void Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd, std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const
/// To create mesh and material kept in <Schape>.
/// \param pShapeNodeElement - reference to node element which kept <Shape> data.
/// \param pNodeMeshInd - reference to list with mesh indices. When pShapeNodeElement will read new mesh index will be added to this list.
/// \param pSceneMeshList - reference to list with meshes. When pShapeNodeElement will read new mesh will be added to this list.
/// \param pSceneMaterialList - reference to list with materials. When pShapeNodeElement will read new material will be added to this list.
void Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd,
std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const;
/// \fn void Postprocess_CollectMetadata(aiNode& pSceneNode, const CX3DImporter_NodeElement& pNodeElement) const
/// Check if child elements of node element is metadata and add it to scene node.
/// \param [in] pNodeElement - node element where metadata is searching.
/// \param [out] pSceneNode - scene node in which metadata will be added.
void Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode) const;
/***********************************************/
/************* Functions: throw set ************/
/***********************************************/
/// \fn void Throw_ArgOutOfRange(const std::string& pArgument)
/// Call that function when argument is out of range and exception must be raised.
/// \throw DeadlyImportError.
/// \param [in] pArgument - argument name.
void Throw_ArgOutOfRange(const std::string& pArgument);
/// \fn void Throw_CloseNotFound(const std::string& pNode)
/// Call that function when close tag of node not found and exception must be raised.
/// E.g.:
/// <Scene>
/// <Shape>
/// </Scene> <!--- shape not closed --->
/// \throw DeadlyImportError.
/// \param [in] pNode - node name in which exception happened.
void Throw_CloseNotFound(const std::string& pNode);
/// \fn void Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
/// Call that function when string value can not be converted to floating point value and exception must be raised.
/// \param [in] pAttrValue - attribute value.
/// \throw DeadlyImportError.
void Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue);
/// \fn void Throw_DEF_And_USE()
/// Call that function when in node defined attributes "DEF" and "USE" and exception must be raised.
/// E.g.: <Box DEF="BigBox" USE="MegaBox">
/// \throw DeadlyImportError.
void Throw_DEF_And_USE();
/// \fn void Throw_IncorrectAttr(const std::string& pAttrName)
/// Call that function when attribute name is incorrect and exception must be raised.
/// \param [in] pAttrName - attribute name.
/// \throw DeadlyImportError.
void Throw_IncorrectAttr(const std::string& pAttrName);
/// \fn void Throw_IncorrectAttrValue(const std::string& pAttrName)
/// Call that function when attribute value is incorrect and exception must be raised.
/// \param [in] pAttrName - attribute name.
/// \throw DeadlyImportError.
void Throw_IncorrectAttrValue(const std::string& pAttrName);
/// \fn void Throw_MoreThanOnceDefined(const std::string& pNode, const std::string& pDescription)
/// Call that function when some type of nodes are defined twice or more when must be used only once and exception must be raised.
/// E.g.:
/// <Shape>
/// <Box/> <!--- first geometry node --->
/// <Sphere/> <!--- second geometry node. raise exception --->
/// </Shape>
/// \throw DeadlyImportError.
/// \param [in] pNodeType - type of node which defined one more time.
/// \param [in] pDescription - message about error. E.g. what the node defined while exception raised.
void Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription);
/// \fn void Throw_TagCountIncorrect(const std::string& pNode)
/// Call that function when count of opening and closing tags which create group(e.g. <Group>) are not equal and exception must be raised.
/// E.g.:
/// <Scene>
/// <Transform> <!--- first grouping node begin --->
/// <Group> <!--- second grouping node begin --->
/// </Transform> <!--- first grouping node end --->
/// </Scene> <!--- one grouping node still not closed --->
/// \throw DeadlyImportError.
/// \param [in] pNode - node name in which exception happened.
void Throw_TagCountIncorrect(const std::string& pNode);
/// \fn void Throw_USE_NotFound(const std::string& pAttrValue)
/// Call that function when defined in "USE" element are not found in graph and exception must be raised.
/// \param [in] pAttrValue - "USE" attribute value.
/// \throw DeadlyImportError.
void Throw_USE_NotFound(const std::string& pAttrValue);
/***********************************************/
/************** Functions: LOG set *************/
/***********************************************/
/// \fn void LogInfo(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->info()
void LogInfo(const std::string& pMessage) { DefaultLogger::get()->info(pMessage); }
/// \fn void LogWarning(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->warn()
void LogWarning(const std::string& pMessage) { DefaultLogger::get()->warn(pMessage); }
/// \fn void LogError(const std::string& pMessage)
/// Short variant for calling \ref DefaultLogger::get()->error()
void LogError(const std::string& pMessage) { DefaultLogger::get()->error(pMessage); }
/***********************************************/
/************** Functions: XML set *************/
/***********************************************/
/// \fn void XML_CheckNode_MustBeEmpty()
/// Chek if current node is empty: <node />. If not then exception will throwed.
void XML_CheckNode_MustBeEmpty();
/// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName)
/// Chek if current node name is equal to pNodeName.
/// \param [in] pNodeName - name for checking.
/// return true if current node name is equal to pNodeName, else - false.
bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }
/// \fn void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
/// Skip unsupported node and report about that. Depend on node name can be skipped begin tag of node all whole node.
/// \param [in] pParentNodeName - parent node name. Used for reporting.
void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName);
/// \fn bool XML_SearchNode(const std::string& pNodeName)
/// Search for specified node in file. XML file read pointer(mReader) will point to found node or file end after search is end.
/// \param [in] pNodeName - requested node name.
/// return true - if node is found, else - false.
bool XML_SearchNode(const std::string& pNodeName);
/// \fn bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
/// \fn float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx);
/// \fn int32_t XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \return read data.
int32_t XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx);
/// \fn void XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListBool(const int pAttrIdx, std::list<bool>& pValue)
void XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
void XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
void XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
void XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
void XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
void XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
void XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue)
/// \overload void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
void XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue);
/// \fn void XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue)
/// Read attribute value.
/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
/// \param [out] pValue - read data.
void XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue);
/***********************************************/
/******* Functions: geometry helper set *******/
/***********************************************/
/// \fn aiVector3D GeometryHelper_Make_Point2D(const float pAngle, const float pRadius)
/// Make point on surface oXY.
/// \param [in] pAngle - angle in radians between radius-vector of point and oX axis. Angle extends from the oX axis counterclockwise to the radius-vector.
/// \param [in] pRadius - length of radius-vector.
/// \return made point coordinates.
aiVector3D GeometryHelper_Make_Point2D(const float pAngle, const float pRadius);
/// \fn void GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments, std::list<aiVector3D>& pVertices)
/// Make 2D figure - linear circular arc with center in (0, 0). The z-coordinate is 0. The arc extends from the pStartAngle counterclockwise
/// to the pEndAngle. If pStartAngle and pEndAngle have the same value, a circle is specified. If the absolute difference between pStartAngle
/// and pEndAngle is greater than or equal to 2pi, a circle is specified.
/// \param [in] pStartAngle - angle in radians of start of the arc.
/// \param [in] pEndAngle - angle in radians of end of the arc.
/// \param [in] pRadius - radius of the arc.
/// \param [out] pNumSegments - number of segments in arc. In other words - tesselation factor.
/// \param [out] pVertices - generated vertices.
void GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments, std::list<aiVector3D>& pVertices);
/// \fn void GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine)
/// Create line set from point set.
/// \param [in] pPoint - input points list.
/// \param [out] pLine - made lines list.
void GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine);
/// \fn GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx)
/// Create CoordIdx of line set from CoordIdx of polyline set.
/// \param [in] pPolylineCoordIdx - vertices indices divided by delimiter "-1". Must contain faces with two or more indices.
/// \param [out] pLineCoordIdx - made CoordIdx of line set.
void GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx);
/// \fn void GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices)
/// Make 3D body - rectangular parallelepiped with center in (0, 0). QL mean quadlist (\sa pVertices).
/// \param [in] pSize - scale factor for body for every axis. E.g. (1, 2, 1) mean: X-size and Z-size - 1, Y-size - 2.
/// \param [out] pVertices - generated vertices. The list of vertices is grouped in quads.
void GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices);
/// \fn void GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
/// Create faces array from vertices indices array.
/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
/// \param [in] pFaces - created faces array.
/// \param [in] pPrimitiveTypes - type of primitives in faces.
void GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const;
/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
/// Add colors to mesh.
/// a. If colorPerVertex is FALSE, colours are applied to each face, as follows:
/// If the colorIndex field is not empty, one colour is used for each face of the mesh. There shall be at least as many indices in the
/// colorIndex field as there are faces in the mesh. The colorIndex field shall not contain any negative entries.
/// If the colorIndex field is empty, the colours in the X3DColorNode node are applied to each face of the mesh in order.
/// There shall be at least as many colours in the X3DColorNode node as there are faces.
/// b. If colorPerVertex is TRUE, colours are applied to each vertex, as follows:
/// If the colorIndex field is not empty, colours are applied to each vertex of the mesh in exactly the same manner that the coordIndex
/// field is used to choose coordinates for each vertex from the <Coordinate> node. The colorIndex field shall contain end-of-face markers (1)
/// in exactly the same places as the coordIndex field.
/// If the colorIndex field is empty, the coordIndex field is used to choose colours from the X3DColorNode node.
/// \param [in] pMesh - mesh for adding data.
/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
/// \param [in] pColorIdx - color indices for every vertex divided by delimiter "-1" if \ref pColorPerVertex is true. if \ref pColorPerVertex is false
/// then pColorIdx contain color indices for every faces and must not contain delimiter "-1".
/// \param [in] pColors - defined colors.
/// \param [in] pColorPerVertex - if \ref pColorPerVertex is true then color in \ref pColors defined for every vertex, if false - for every face.
void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
/// \overload void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
/// Add colors to mesh.
/// \param [in] pMesh - mesh for adding data.
/// \param [in] pColors - defined colors.
/// \param [in] pColorPerVertex - if \ref pColorPerVertex is true then color in \ref pColors defined for every vertex, if false - for every face.
void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
/// \overload void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
/// \fn void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
/// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx,
const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
/// \fn void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
/// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
/// \fn void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx, const std::list<aiVector2D>& pTexCoords) const
/// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
const std::list<aiVector2D>& pTexCoords) const;
/// \fn void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const
/// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const;
/// \fn aiMesh* GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
/// Create mesh.
/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
/// \param [in] pVertices - vertices of mesh.
/// \return created mesh.
aiMesh* GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const;
/***********************************************/
/******** Functions: parse set private *********/
/***********************************************/
/// \fn void ParseHelper_Group_Begin()
/// Create node element with type "Node" in scene graph. That operation is needed when you enter to X3D group node
/// like <Group>, <Transform> etc. When exiting from X3D group node(e.g. </Group>) \ref ParseHelper_Node_Exit must
/// be called.
/// \param [in] pStatic - flag: if true then static node is created(e.g. <StaticGroup>).
void ParseHelper_Group_Begin(const bool pStatic = false);
/// \fn void ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode)
/// Make pNode as current and enter deeper for parsing child nodes. At end \ref ParseHelper_Node_Exit must be called.
/// \param [in] pNode - new current node.
void ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode);
/// \fn void ParseHelper_Group_End()
/// This function must be called when exiting from X3D group node(e.g. </Group>). \ref ParseHelper_Group_Begin.
void ParseHelper_Node_Exit();
/// \fn void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
/// Attribute values of floating point types can take form ".x"(without leading zero). irrXMLReader can not read this form of values and it
/// must be converted to right form - "0.xxx".
/// \param [in] pInStr - pointer to input string which can contain incorrect form of values.
/// \param [out[ pOutString - output string with right form of values.
void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString);
/// \fn bool ParseHelper_CheckRead_X3DMetadataObject()
/// Check if current node has nodes of type X3DMetadataObject. Why we must do it? Because X3DMetadataObject can be in any non-empty X3DNode.
/// Meaning that X3DMetadataObject can be in any non-empty node in <Scene>.
/// \return true - if metadata node are found and parsed, false - metadata not found.
bool ParseHelper_CheckRead_X3DMetadataObject();
/// \fn bool ParseHelper_CheckRead_X3DMetadataObject()
/// Check if current node has nodes of type X3DGeometricPropertyNode. X3DGeometricPropertyNode
/// X3DGeometricPropertyNode inheritors:
/// <FogCoordinate>, <HAnimDisplacer>, <Color>, <ColorRGBA>, <Coordinate>, <CoordinateDouble>, <GeoCoordinate>, <Normal>,
/// <MultiTextureCoordinate>, <TextureCoordinate>, <TextureCoordinate3D>, <TextureCoordinate4D>, <TextureCoordinateGenerator>,
/// <FloatVertexAttribute>, <Matrix3VertexAttribute>, <Matrix4VertexAttribute>.
/// \return true - if nodes are found and parsed, false - nodes not found.
bool ParseHelper_CheckRead_X3DGeometricPropertyNode();
/// \fn void ParseNode_Root()
/// Parse <X3D> node of the file.
void ParseNode_Root();
/// \fn void ParseNode_Head()
/// Parse <head> node of the file.
void ParseNode_Head();
/// \fn void ParseNode_Root()
/// Parse <Scene> node of the file.
void ParseNode_Scene();
/// \fn void ParseNode_Metadata(CX3DImporter_NodeElement* pParent, const std::string& pNodeName)
/// Parse child nodes of <Metadata*> node.
/// \param [in] pNodeName - parsed node name. Must be set because that function is general and name needed for checking the end
/// and error reporing.
/// \param [in] pParentElement - parent metadata element.
void ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& pNodeName);
/// \fn void ParseNode_MetadataBoolean()
/// Parse <MetadataBoolean> node of the file.
void ParseNode_MetadataBoolean();
/// \fn void ParseNode_MetadataDouble()
/// Parse <MetadataDouble> node of the file.
void ParseNode_MetadataDouble();
/// \fn void ParseNode_MetadataFloat()
/// Parse <MetadataFloat> node of the file.
void ParseNode_MetadataFloat();
/// \fn void ParseNode_MetadataInteger()
/// Parse <MetadataInteger> node of the file.
void ParseNode_MetadataInteger();
/// \fn void ParseNode_MetadataSet()
/// Parse <MetadataSet> node of the file.
void ParseNode_MetadataSet();
/// \fn void ParseNode_MetadataString()
/// Parse <MetadataString> node of the file.
void ParseNode_MetadataString();
/// \fn void ParseNode_Geometry2D_Arc2D()
/// Parse <Arc2D> node of the file.
void ParseNode_Geometry2D_Arc2D();
/// \fn void ParseNode_Geometry2D_ArcClose2D()
/// Parse <ArcClose2D> node of the file.
void ParseNode_Geometry2D_ArcClose2D();
/// \fn void ParseNode_Geometry2D_Circle2D()
/// Parse <Circle2D> node of the file.
void ParseNode_Geometry2D_Circle2D();
/// \fn void ParseNode_Geometry2D_Disk2D()
/// Parse <Disk2D> node of the file.
void ParseNode_Geometry2D_Disk2D();
/// \fn void ParseNode_Geometry2D_Polyline2D()
/// Parse <Polyline2D> node of the file.
void ParseNode_Geometry2D_Polyline2D();
/// \fn void ParseNode_Geometry2D_Polypoint2D()
/// Parse <Polypoint2D> node of the file.
void ParseNode_Geometry2D_Polypoint2D();
/// \fn void ParseNode_Geometry2D_Rectangle2D()
/// Parse <Rectangle2D> node of the file.
void ParseNode_Geometry2D_Rectangle2D();
/// \fn void ParseNode_Geometry2D_TriangleSet2D()
/// Parse <TriangleSet2D> node of the file.
void ParseNode_Geometry2D_TriangleSet2D();
/// \fn void ParseNode_Geometry3D_Box()
/// Parse <Box> node of the file.
void ParseNode_Geometry3D_Box();
/// \fn void ParseNode_Geometry3D_Cone()
/// Parse <Cone> node of the file.
void ParseNode_Geometry3D_Cone();
/// \fn void ParseNode_Geometry3D_Cylinder()
/// Parse <Cylinder> node of the file.
void ParseNode_Geometry3D_Cylinder();
/// \fn void ParseNode_Geometry3D_ElevationGrid()
/// Parse <ElevationGrid> node of the file.
void ParseNode_Geometry3D_ElevationGrid();
/// \fn void ParseNode_Geometry3D_Extrusion()
/// Parse <Extrusion> node of the file.
void ParseNode_Geometry3D_Extrusion();
/// \fn void ParseNode_Geometry3D_IndexedFaceSet()
/// Parse <IndexedFaceSet> node of the file.
void ParseNode_Geometry3D_IndexedFaceSet();
/// \fn void ParseNode_Geometry3D_Sphere()
/// Parse <Sphere> node of the file.
void ParseNode_Geometry3D_Sphere();
/// \fn void ParseNode_Grouping_Group()
/// Parse <Group> node of the file. And create new node in scene graph.
void ParseNode_Grouping_Group();
/// \fn void ParseNode_Grouping_GroupEnd()
/// Doing actions at an exit from <Group>. Walk up in scene graph.
void ParseNode_Grouping_GroupEnd();
/// \fn void ParseNode_Grouping_StaticGroup()
/// Parse <StaticGroup> node of the file. And create new node in scene graph.
void ParseNode_Grouping_StaticGroup();
/// \fn void ParseNode_Grouping_StaticGroupEnd()
/// Doing actions at an exit from <StaticGroup>. Walk up in scene graph.
void ParseNode_Grouping_StaticGroupEnd();
/// \fn void ParseNode_Grouping_Switch()
/// Parse <Switch> node of the file. And create new node in scene graph.
void ParseNode_Grouping_Switch();
/// \fn void ParseNode_Grouping_SwitchEnd()
/// Doing actions at an exit from <Switch>. Walk up in scene graph.
void ParseNode_Grouping_SwitchEnd();
/// \fn void ParseNode_Grouping_Transform()
/// Parse <Transform> node of the file. And create new node in scene graph.
void ParseNode_Grouping_Transform();
/// \fn void ParseNode_Grouping_TransformEnd()
/// Doing actions at an exit from <Transform>. Walk up in scene graph.
void ParseNode_Grouping_TransformEnd();
/// \fn void ParseNode_Rendering_Color()
/// Parse <Color> node of the file.
void ParseNode_Rendering_Color();
/// \fn void ParseNode_Rendering_ColorRGBA()
/// Parse <ColorRGBA> node of the file.
void ParseNode_Rendering_ColorRGBA();
/// \fn void ParseNode_Rendering_Coordinate()
/// Parse <Coordinate> node of the file.
void ParseNode_Rendering_Coordinate();
/// \fn void ParseNode_Rendering_Normal()
/// Parse <Normal> node of the file.
void ParseNode_Rendering_Normal();
/// \fn void ParseNode_Rendering_IndexedLineSet()
/// Parse <IndexedLineSet> node of the file.
void ParseNode_Rendering_IndexedLineSet();
/// \fn void ParseNode_Rendering_IndexedTriangleFanSet()
/// Parse <IndexedTriangleFanSet> node of the file.
void ParseNode_Rendering_IndexedTriangleFanSet();
/// \fn void ParseNode_Rendering_IndexedTriangleSet()
/// Parse <IndexedTriangleSet> node of the file.
void ParseNode_Rendering_IndexedTriangleSet();
/// \fn void ParseNode_Rendering_IndexedTriangleStripSet()
/// Parse <IndexedTriangleStripSet> node of the file.
void ParseNode_Rendering_IndexedTriangleStripSet();
/// \fn void ParseNode_Rendering_LineSet()
/// Parse <LineSet> node of the file.
void ParseNode_Rendering_LineSet();
/// \fn void ParseNode_Rendering_PointSet()
/// Parse <PointSet> node of the file.
void ParseNode_Rendering_PointSet();
/// \fn void ParseNode_Rendering_TriangleFanSet()
/// Parse <TriangleFanSet> node of the file.
void ParseNode_Rendering_TriangleFanSet();
/// \fn void ParseNode_Rendering_TriangleSet()
/// Parse <TriangleSet> node of the file.
void ParseNode_Rendering_TriangleSet();
/// \fn void ParseNode_Rendering_TriangleStripSet()
/// Parse <TriangleStripSet> node of the file.
void ParseNode_Rendering_TriangleStripSet();
/// \fn void ParseNode_Texturing_ImageTexture()
/// Parse <ImageTexture> node of the file.
void ParseNode_Texturing_ImageTexture();
/// \fn void ParseNode_Texturing_TextureCoordinate()
/// Parse <TextureCoordinate> node of the file.
void ParseNode_Texturing_TextureCoordinate();
/// \fn void ParseNode_Texturing_TextureTransform()
/// Parse <TextureTransform> node of the file.
void ParseNode_Texturing_TextureTransform();
/// \fn void ParseNode_Shape_Shape()
/// Parse <Shape> node of the file.
void ParseNode_Shape_Shape();
/// \fn void ParseNode_Shape_Appearance()
/// Parse <Appearance> node of the file.
void ParseNode_Shape_Appearance();
/// \fn void ParseNode_Shape_Material()
/// Parse <Material> node of the file.
void ParseNode_Shape_Material();
/// \fn void ParseNode_Networking_Inline()
/// Parse <Inline> node of the file.
void ParseNode_Networking_Inline();
/// \fn void ParseNode_Lighting_DirectionalLight()
/// Parse <DirectionalLight> node of the file.
void ParseNode_Lighting_DirectionalLight();
/// \fn void ParseNode_Lighting_PointLight()
/// Parse <PointLight> node of the file.
void ParseNode_Lighting_PointLight();
/// \fn void ParseNode_Lighting_SpotLight()
/// Parse <SpotLight> node of the file.
void ParseNode_Lighting_SpotLight();
public:
/// \fn X3DImporter()
/// Default constructor.
X3DImporter()
: NodeElement_Cur(nullptr), mReader(nullptr)
{}
/// \fn ~X3DImporter()
/// Default destructor.
~X3DImporter();
/***********************************************/
/******** Functions: parse set, public *********/
/***********************************************/
/// \fn void ParseFile(const std::string& pFile, IOSystem* pIOHandler)
/// Parse X3D file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
/// Also exception can be throwed if trouble will found.
/// \param [in] pFile - name of file to be parsed.
/// \param [in] pIOHandler - pointer to IO helper object.
void ParseFile(const std::string& pFile, IOSystem* pIOHandler);
/***********************************************/
/********* Functions: BaseImporter set *********/
/***********************************************/
bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const;
void GetExtensionList(std::set<std::string>& pExtensionList);
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
const aiImporterDesc* GetInfo ()const;
};// class X3DImporter
}// namespace Assimp
#endif // INCLUDED_AI_X3D_IMPORTER_H

View File

@ -0,0 +1,481 @@
/// \file X3DImporter_Geometry2D.cpp
/// \brief Parsing data from nodes of "Geometry2D" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Node.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <Arc2D
// DEF="" ID
// USE="" IDREF
// endAngle="1.570796" SFFloat [initializeOnly]
// radius="1" SFFloat [initializeOnly]
// startAngle="0" SFFloat [initializeOnly]
// />
// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle
// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different
// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified.
void X3DImporter::ParseNode_Geometry2D_Arc2D()
{
std::string def, use;
float endAngle = AI_MATH_HALF_PI_F;
float radius = 1;
float startAngle = 0;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// create point list of geometry object and convert it to line set.
std::list<aiVector3D> tlist;
GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist);///TODO: IME - AI_CONFIG for NumSeg
GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Arc2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <ArcClose2D
// DEF="" ID
// USE="" IDREF
// closureType="PIE" SFString [initializeOnly], {"PIE", "CHORD"}
// endAngle="1.570796" SFFloat [initializeOnly]
// radius="1" SFFloat [initializeOnly]
// solid="false" SFBool [initializeOnly]
// startAngle="0" SFFloat [initializeOnly]
// />
// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius
// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater
// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has
// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between
// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center.
// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then
// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point
// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when
// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
void X3DImporter::ParseNode_Geometry2D_ArcClose2D()
{
std::string def, use;
std::string closureType("PIE");
float endAngle = AI_MATH_HALF_PI_F;
float radius = 1;
bool solid = false;
float startAngle = 0;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
// create point list of geometry object.
GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);///TODO: IME - AI_CONFIG for NumSeg
// add chord or two radiuses only if not a circle was defined
if(!((fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle)))
{
std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
if((closureType == "PIE") || (closureType == "\"PIE\""))
vlist.push_back(aiVector3D(0, 0, 0));// center point - first radial line
else if((closureType != "CHORD") && (closureType != "\"CHORD\""))
Throw_IncorrectAttrValue("closureType");
vlist.push_back(*vlist.begin());// arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE).
}
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.size();
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "ArcClose2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Circle2D
// DEF="" ID
// USE="" IDREF
// radius="1" SFFloat [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry2D_Circle2D()
{
std::string def, use;
float radius = 1;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// create point list of geometry object and convert it to line set.
std::list<aiVector3D> tlist;
GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist);///TODO: IME - AI_CONFIG for NumSeg
GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Circle2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Disk2D
// DEF="" ID
// USE="" IDREF
// innerRadius="0" SFFloat [initializeOnly]
// outerRadius="1" SFFloat [initializeOnly]
// solid="false" SFBool [initializeOnly]
// />
// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the
// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero.
// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely
// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall
// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of
// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
void X3DImporter::ParseNode_Geometry2D_Disk2D()
{
std::string def, use;
float innerRadius = 0;
float outerRadius = 1;
bool solid = false;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne);
}
else
{
std::list<aiVector3D> tlist_o, tlist_i;
if(innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// create point list of geometry object.
///TODO: IME - AI_CONFIG for NumSeg
GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o);// outer circle
if(innerRadius == 0.0f)
{// make filled disk
// in tlist_o we already have points of circle. just copy it and sign as polygon.
((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices = tlist_o;
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = tlist_o.size();
}
else if(innerRadius == outerRadius)
{// make circle
// in tlist_o we already have points of circle. convert it to line set.
GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
}
else
{// make disk
std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i);// inner circle
//
// create quad list from two point lists
//
if(tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list.");// tlist_i and tlist_o has equal size.
// add all quads except last
for(std::list<aiVector3D>::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();)
{
// do not forget - CCW direction
vlist.push_back(*it_i++);// 1st point
vlist.push_back(*it_o++);// 2nd point
vlist.push_back(*it_o);// 3rd point
vlist.push_back(*it_i);// 4th point
}
// add last quad
vlist.push_back(*tlist_i.end());// 1st point
vlist.push_back(*tlist_o.end());// 2nd point
vlist.push_back(*tlist_o.begin());// 3rd point
vlist.push_back(*tlist_o.begin());// 4th point
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 4;
}
((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Disk2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Polyline2D
// DEF="" ID
// USE="" IDREF
// lineSegments="" MFVec2F [intializeOnly]
// />
void X3DImporter::ParseNode_Geometry2D_Polyline2D()
{
std::string def, use;
std::list<aiVector2D> lineSegments;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
//
// convert read point list of geometry object to line set.
//
std::list<aiVector3D> tlist;
// convert vec2 to vec3
for(std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); it2++) tlist.push_back(aiVector3D(it2->x, it2->y, 0));
// convert point set to line set
GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Polyline2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Polypoint2D
// DEF="" ID
// USE="" IDREF
// point="" MFVec2F [inputOutput]
// />
void X3DImporter::ParseNode_Geometry2D_Polypoint2D()
{
std::string def, use;
std::list<aiVector2D> point;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// convert vec2 to vec3
for(std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); it2++)
{
((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
}
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 1;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Polypoint2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Rectangle2D
// DEF="" ID
// USE="" IDREF
// size="2 2" SFVec2f [initializeOnly]
// solid="false" SFBool [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry2D_Rectangle2D()
{
std::string def, use;
aiVector2D size(2, 2);
bool solid = false;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
float x1 = -size.x / 2.0f;
float x2 = size.x / 2.0f;
float y1 = -size.y / 2.0f;
float y2 = size.y / 2.0f;
std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
vlist.push_back(aiVector3D(x2, y1, 0));// 1st point
vlist.push_back(aiVector3D(x2, y2, 0));// 2nd point
vlist.push_back(aiVector3D(x1, y2, 0));// 3rd point
vlist.push_back(aiVector3D(x1, y1, 0));// 4th point
((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 4;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Rectangle2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TriangleSet2D
// DEF="" ID
// USE="" IDREF
// solid="false" SFBool [initializeOnly]
// vertices="" MFVec2F [inputOutput]
// />
void X3DImporter::ParseNode_Geometry2D_TriangleSet2D()
{
std::string def, use;
bool solid = false;
std::list<aiVector2D> vertices;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne);
}
else
{
if(vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// convert vec2 to vec3
for(std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); it2++)
{
((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
}
((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 3;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "TriangleSet2D");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,953 @@
/// \file X3DImporter_Geometry3D.cpp
/// \brief Parsing data from nodes of "Geometry3D" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
// Header files, Assimp.
#include "StandardShapes.h"
namespace Assimp
{
// <Box
// DEF="" ID
// USE="" IDREF
// size="2 2 2" SFVec3f [initializeOnly]
// solid="true" SFBool [initializeOnly]
// />
// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes.
// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes
// respectively and each component value shall be greater than zero.
void X3DImporter::ParseNode_Geometry3D_Box()
{
std::string def, use;
bool solid = true;
aiVector3D size(2, 2, 2);
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices);// get quad list
((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 4;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Box");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Cone
// DEF="" ID
// USE="" IDREF
// bottom="true" SFBool [initializeOnly]
// bottomRadius="1" SFloat [initializeOnly]
// height="2" SFloat [initializeOnly]
// side="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry3D_Cone()
{
std::string use, def;
bool bottom = true;
float bottomRadius = 1;
float height = 2;
bool side = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne);
}
else
{
const unsigned int tess = 30;///TODO: IME tesselation factor thru ai_property
std::vector<aiVector3D> tvec;// temp array for vertices.
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// make cone or parts according to flags.
if(side)
{
StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom);
}
else if(bottom)
{
StandardShapes::MakeCircle(bottomRadius, tess, tvec);
height = -(height / 2);
for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) it->y = height;// y - because circle made in oXZ.
}
// copy data from temp array
for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it);
((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Cone");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Cylinder
// DEF="" ID
// USE="" IDREF
// bottom="true" SFBool [initializeOnly]
// height="2" SFloat [initializeOnly]
// radius="1" SFloat [initializeOnly]
// side="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// top="true" SFBool [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry3D_Cylinder()
{
std::string use, def;
bool bottom = true;
float height = 2;
float radius = 1;
bool side = true;
bool solid = true;
bool top = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne);
}
else
{
const unsigned int tess = 30;///TODO: IME tesselation factor thru ai_property
std::vector<aiVector3D> tside;// temp array for vertices of side.
std::vector<aiVector3D> tcir;// temp array for vertices of circle.
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// make cilynder or parts according to flags.
if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true);
height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height.
if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir);
// copy data from temp arrays
std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices;// just short alias.
for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); it++) vlist.push_back(*it);
if(top)
{
for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
{
(*it).y = height;// y - because circle made in oXZ.
vlist.push_back(*it);
}
}// if(top)
if(bottom)
{
for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
{
(*it).y = -height;// y - because circle made in oXZ.
vlist.push_back(*it);
}
}// if(top)
((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Cylinder");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <ElevationGrid
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// creaseAngle="0" SFloat [initializeOnly]
// height="" MFloat [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// xDimension="0" SFInt32 [initializeOnly]
// xSpacing="1.0" SFloat [initializeOnly]
// zDimension="0" SFInt32 [initializeOnly]
// zSpacing="1.0" SFloat [initializeOnly]
// >
// <!-- ColorNormalTexCoordContentModel -->
// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single
// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </ElevationGrid>
// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described
// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate
// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero.
// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals.
void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
float creaseAngle = 0;
std::list<float> height;
bool normalPerVertex = true;
bool solid = true;
int32_t xDimension = 0;
float xSpacing = 1;
int32_t zDimension = 0;
float zSpacing = 1;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsListF);
MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32);
MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32);
MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne);
}
else
{
if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero.");
if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero.");
if((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\"");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_ElevationGrid& grid_alias = *((CX3DImporter_NodeElement_ElevationGrid*)ne);// create alias for conveience
{// create grid vertices list
std::list<float>::const_iterator he_it = height.begin();
for(int32_t zi = 0; zi < zDimension; zi++)// rows
{
for(int32_t xi = 0; xi < xDimension; xi++)// columns
{
aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
grid_alias.Vertices.push_back(tvec);
he_it++;
}
}
}// END: create grid vertices list
//
// create faces list. In "coordIdx" format
//
// check if we have quads
if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set.
{
((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 2;// will be holded as line set.
for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++)
{
grid_alias.CoordIdx.push_back(i);
grid_alias.CoordIdx.push_back(i + 1);
grid_alias.CoordIdx.push_back(-1);
}
}
else// two or more elements in every dimension is set. create quad set.
{
((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 4;
for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows
{
for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns
{
// points direction in face.
if(ccw)
{
// CCW:
// 3 2
// 0 1
grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
}
else
{
// CW:
// 0 1
// 3 2
grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
}// if(ccw) else
grid_alias.CoordIdx.push_back(-1);
}// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)
}// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)
}// if((xDimension < 2) || (zDimension < 2)) else
grid_alias.ColorPerVertex = colorPerVertex;
grid_alias.NormalPerVertex = normalPerVertex;
grid_alias.CreaseAngle = creaseAngle;
grid_alias.Solid = solid;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("ElevationGrid");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid");
MACRO_NODECHECK_LOOPEND("ElevationGrid");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}// if(!mReader->isEmptyElement()) else
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
template<typename TVector>
static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector>& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed)
{
size_t cur_sz = pCurve.size();
pCurveIsClosed = false;
// for curve with less than four points checking is have no sense,
if(cur_sz < 4) return;
for(size_t s = 3, s_e = cur_sz; s < s_e; s++)
{
// search for first point of duplicated part.
if(pCurve[0] == pCurve[s])
{
bool found = true;
// check if tail(indexed by b2) is duplicate of head(indexed by b1).
for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
{
if(pCurve[b1] != pCurve[b2])
{// points not match: clear flag and break loop.
found = false;
break;
}
}// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
// if duplicate tail is found then drop or not it depending on flags.
if(found)
{
pCurveIsClosed = true;
if(pDropTail)
{
if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics.
pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail
}
break;
}// if(found)
}// if(pCurve[0] == pCurve[s])
}// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++)
}
static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed)
{
const size_t spine_idx_last = pSpine.size() - 1;
aiVector3D tvec;
if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases
{
if(pSpine_Closed)
{// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] spine[n 2]) to compute the Y-axis.
// As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0])
// in tail are removed.
// So, last point in pSpine is a spine[n - 2]
tvec = pSpine[1] - pSpine[spine_idx_last];
}
else if(pSpine_PointIdx == 0)
{// The Y-axis used for the first point is the vector from spine[0] to spine[1]
tvec = pSpine[1] - pSpine[0];
}
else
{// The Y-axis used for the last point it is the vector from spine[n2] to spine[n1]. In our case(see above about droping tail) spine[n - 1] is
// the spine[0].
tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1];
}
}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))
else
{// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] spine[i1]).
tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1];
}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else
return tvec.Normalize();
}
static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed,
const aiVector3D pVecZ_Prev)
{
const aiVector3D zero_vec(0);
const size_t spine_idx_last = pSpine.size() - 1;
aiVector3D tvec;
// at first special cases
if(pSpine.size() < 3)// spine have not enough points for vector calculations.
{
tvec.Set(0, 0, 1);
}
else if(pSpine_PointIdx == 0)// special case: first point
{
if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate.
{
tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]);
}
else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z.
{
bool found = false;
// As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear)
// then the Z-axis for the first spine point with a defined Z-axis is used."
// Walk thru spine and find Z.
for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++)
{
// (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1])
tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]);
found = !tvec.Equal(zero_vec);
}
// if entire spine are collinear then use OZ axis.
if(!found) tvec.Set(0, 0, 1);
}// if(pSpine_Closed) else
}// else if(pSpine_PointIdx == 0)
else if(pSpine_PointIdx == spine_idx_last)// special case: last point
{
if(pSpine_Closed)
{// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2].
tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
// if taken spine vectors are collinear then use previous vector Z.
if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
}
else
{// vector Z for last point of not closed curve is previous vector Z.
tvec = pVecZ_Prev;
}
}
else// regular point
{
tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
// if taken spine vectors are collinear then use previous vector Z.
if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
}
// After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis
// is flipped (multiplied by 1).
if((tvec * pVecZ_Prev) < 0) tvec = -tvec;
return tvec.Normalize();
}
// <Extrusion
// DEF="" ID
// USE="" IDREF
// beginCap="true" SFBool [initializeOnly]
// ccw="true" SFBool [initializeOnly]
// convex="true" SFBool [initializeOnly]
// creaseAngle="0.0" SFloat [initializeOnly]
// crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f [initializeOnly]
// endCap="true" SFBool [initializeOnly]
// orientation="0 0 1 0" MFRotation [initializeOnly]
// scale="1 1" MFVec2f [initializeOnly]
// solid="true" SFBool [initializeOnly]
// spine="0 0 0 0 1 0" MFVec3f [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry3D_Extrusion()
{
std::string use, def;
bool beginCap = true;
bool ccw = true;
bool convex = true;
float creaseAngle = 0;
std::vector<aiVector2D> crossSection;
bool endCap = true;
std::vector<float> orientation;
std::vector<aiVector2D> scale;
bool solid = true;
std::vector<aiVector3D> spine;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f);
MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF);
MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne);
}
else
{
//
// check if default values must be assigned
//
if(spine.size() == 0)
{
spine.resize(2);
spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0);
}
else if(spine.size() == 1)
{
throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points.");
}
if(crossSection.size() == 0)
{
crossSection.resize(5);
crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1);
}
{// orientation
size_t ori_size = orientation.size() / 4;
if(ori_size < spine.size())
{
float add_ori[4];// values that will be added
if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points.
{
add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3];
}
else// else - use default values
{
add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0;
}
orientation.reserve(spine.size() * 4);
for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++)
orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]);
}
if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers.");
}// END: orientation
{// scale
if(scale.size() < spine.size())
{
aiVector2D add_sc;
if(scale.size() == 1)// if "scale" has one element then use it value for all spine points.
add_sc = scale[0];
else// else - use default values
add_sc.Set(1, 1);
scale.reserve(spine.size());
for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc);
}
}// END: scale
//
// create and if needed - define new geometry object.
//
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ext_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);// create alias for conveience
// assign part of input data
ext_alias.CCW = ccw;
ext_alias.Convex = convex;
ext_alias.CreaseAngle = creaseAngle;
ext_alias.Solid = solid;
//
// How we done it at all?
// 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector
// are applied vor every basis.
// 2. After that we can create array of point sets: which are scaled, transfered to basis of relative basis and at final translated to real position
// using relative spine point.
// 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if
// needed. While createing CootdIdx is taking in account CCW flag.
// 4. The last step: create Vertices list.
//
bool spine_closed;// flag: true if spine curve is closed.
bool cross_closed;// flag: true if cross curve is closed.
std::vector<aiMatrix3x3> basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z.
std::vector<std::vector<aiVector3D> > pointset_arr;// array of point sets: cross curves.
// detect closed curves
GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end.
GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end.
// If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface.
if(spine_closed)
{
beginCap |= endCap;
endCap = false;
}
{// 1. Calculate array of basises.
aiMatrix4x4 rotmat;
aiVector3D vecX(0), vecY(0), vecZ(0);
basis_arr.resize(spine.size());
for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
{
aiVector3D tvec;
// get axises of basis.
vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed);
vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ);
vecX = (vecY ^ vecZ).Normalize();
// get rotation matrix and apply "orientation" to basis
aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat);
tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z;
tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z;
tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z;
}// for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
}// END: 1. Calculate array of basises
{// 2. Create array of point sets.
aiMatrix4x4 scmat;
std::vector<aiVector3D> tcross(crossSection.size());
pointset_arr.resize(spine.size());
for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
{
aiVector3D tc23vec;
tc23vec.Set(scale[spi].x, 0, scale[spi].y);
aiMatrix4x4::Scaling(tc23vec, scmat);
for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
{
aiVector3D tvecX, tvecY, tvecZ;
tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y);
// apply scaling to point
tcross[cri] = scmat * tc23vec;
//
// transfer point to new basis
// calculate coordinate in new basis
tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x;
tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y;
tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z;
// apply new coordinates and translate it to spine point.
tcross[cri] = tvecX + tvecY + tvecZ + spine[spi];
}// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++)
pointset_arr[spi] = tcross;// store transfered point set
}// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++)
}// END: 2. Create array of point sets.
{// 3. Create CoordIdx.
// add caps if needed
if(beginCap)
{
// add cap as polygon. vertices of cap are places at begin, so just add numbers from zero.
for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(i);
// add delimiter
ext_alias.CoordIndex.push_back(-1);
}// if(beginCap)
if(endCap)
{
// add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset.
size_t beg = (pointset_arr.size() - 1) * crossSection.size();
for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(i);
// add delimiter
ext_alias.CoordIndex.push_back(-1);
}// if(beginCap)
// add quads
for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++)
{
const size_t cr_sz = crossSection.size();
const size_t cr_last = crossSection.size() - 1;
size_t right_col;// hold index basis for points of quad placed in right column;
if(spi != spi_e)
right_col = spi + 1;
else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve.
right_col = 0;
else
break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine.
for(size_t cri = 0; cri < cr_sz; cri++)
{
if(cri != cr_last)
{
MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
spi * cr_sz + cri, right_col * cr_sz + cri, right_col * cr_sz + cri + 1, spi * cr_sz + cri + 1);
// add delimiter
ext_alias.CoordIndex.push_back(-1);
}
else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve.
{
MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
spi * cr_sz + cri, right_col * cr_sz + cri, right_col * cr_sz + 0, spi * cr_sz + 0);
// add delimiter
ext_alias.CoordIndex.push_back(-1);
}
}// for(size_t cri = 0; cri < cr_sz; cri++)
}// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++)
}// END: 3. Create CoordIdx.
{// 4. Create vertices list.
// just copy all vertices
for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
{
for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
{
ext_alias.Vertices.push_back(pointset_arr[spi][cri]);
}
}
}// END: 4. Create vertices list.
//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex);
//PrintVectorSet("Ext. Vertices", ext_alias.Vertices);
// check for child nodes
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Extrusion");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <IndexedFaceSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorIndex="" MFInt32 [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// convex="true" SFBool [initializeOnly]
// coordIndex="" MFInt32 [initializeOnly]
// creaseAngle="0" SFFloat [initializeOnly]
// normalIndex="" MFInt32 [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// texCoordIndex="" MFInt32 [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </IndexedFaceSet>
void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet()
{
std::string use, def;
bool ccw = true;
std::list<int32_t> colorIndex;
bool colorPerVertex = true;
bool convex = true;
std::list<int32_t> coordIndex;
float creaseAngle = 0;
std::list<int32_t> normalIndex;
bool normalPerVertex = true;
bool solid = true;
std::list<int32_t> texCoordIndex;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne);
}
else
{
// check data
if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorIndex = colorIndex;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.Convex = convex;
ne_alias.CoordIndex = coordIndex;
ne_alias.CreaseAngle = creaseAngle;
ne_alias.NormalIndex = normalIndex;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
ne_alias.TexCoordIndex = texCoordIndex;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet");
MACRO_NODECHECK_LOOPEND("IndexedFaceSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Sphere
// DEF="" ID
// USE="" IDREF
// radius="1" SFloat [initializeOnly]
// solid="true" SFBool [initializeOnly]
// />
void X3DImporter::ParseNode_Geometry3D_Sphere()
{
std::string use, def;
float radius = 1;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne);
}
else
{
const unsigned int tess = 3;///TODO: IME tesselation factor thru ai_property
std::vector<aiVector3D> tlist;
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
StandardShapes::MakeSphere(tess, tlist);
// copy data from temp array and apply scale
for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++)
{
((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it * radius);
}
((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Sphere");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,269 @@
/// \file X3DImporter_Group.cpp
/// \brief Parsing data from nodes of "Grouping" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <Group
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// >
// <!-- ChildContentModel -->
// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
// precise palette of legal nodes that are available depends on assigned profile and components.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </Group>
// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform.
void X3DImporter::ParseNode_Grouping_Group()
{
std::string def, use;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
CX3DImporter_NodeElement* ne;
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
}
else
{
ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
// at this place new group mode created and made current, so we can name it.
if(!def.empty()) NodeElement_Cur->ID = def;
// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
// for empty element exit from node in that place
if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
}// if(!use.empty()) else
}
void X3DImporter::ParseNode_Grouping_GroupEnd()
{
ParseHelper_Node_Exit();// go up in scene graph
}
// <StaticGroup
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// >
// <!-- ChildContentModel -->
// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
// precise palette of legal nodes that are available depends on assigned profile and components.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </StaticGroup>
// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or
// contain any USE references outside the StaticGroup.
void X3DImporter::ParseNode_Grouping_StaticGroup()
{
std::string def, use;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
CX3DImporter_NodeElement* ne;
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
}
else
{
ParseHelper_Group_Begin(true);// create new grouping element and go deeper if node has children.
// at this place new group mode created and made current, so we can name it.
if(!def.empty()) NodeElement_Cur->ID = def;
// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
// for empty element exit from node in that place
if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
}// if(!use.empty()) else
}
void X3DImporter::ParseNode_Grouping_StaticGroupEnd()
{
ParseHelper_Node_Exit();// go up in scene graph
}
// <Switch
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// whichChoice="-1" SFInt32 [inputOutput]
// >
// <!-- ChildContentModel -->
// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
// precise palette of legal nodes that are available depends on assigned profile and components.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </Switch>
// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child
// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing
// is chosen.
void X3DImporter::ParseNode_Grouping_Switch()
{
std::string def, use;
int32_t whichChoice = -1;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
CX3DImporter_NodeElement* ne;
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
}
else
{
ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
// at this place new group mode created and made current, so we can name it.
if(!def.empty()) NodeElement_Cur->ID = def;
// also set values specific to this type of group
((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->UseChoice = true;
((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Choice = whichChoice;
// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
// for empty element exit from node in that place
if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
}// if(!use.empty()) else
}
void X3DImporter::ParseNode_Grouping_SwitchEnd()
{
// just exit from node. Defined choice will be accepted at postprocessing stage.
ParseHelper_Node_Exit();// go up in scene graph
}
// <Transform
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// center="0 0 0" SFVec3f [inputOutput]
// rotation="0 0 1 0" SFRotation [inputOutput]
// scale="1 1 1" SFVec3f [inputOutput]
// scaleOrientation="0 0 1 0" SFRotation [inputOutput]
// translation="0 0 0" SFVec3f [inputOutput]
// >
// <!-- ChildContentModel -->
// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
// precise palette of legal nodes that are available depends on assigned profile and components.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </Transform>
// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate
// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the
// equivalent transformation matrices,
// P' = T * C * R * SR * S * -SR * -C * P
void X3DImporter::ParseNode_Grouping_Transform()
{
aiVector3D center(0, 0, 0);
float rotation[4] = {0, 0, 1, 0};
aiVector3D scale(1, 1, 1);// A value of zero indicates that any child geometry shall not be displayed
float scale_orientation[4] = {0, 0, 1, 0};
aiVector3D translation(0, 0, 0);
aiMatrix4x4 matr, tmatr;
std::string use, def;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f);
if(an == "rotation")
{
std::vector<float> tvec;
XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
if(tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements.");
memcpy(rotation, tvec.data(), sizeof(rotation));
continue;
}
if(an == "scaleOrientation")
{
std::vector<float> tvec;
XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
if(tvec.size() != 4) throw DeadlyImportError("<Transform>: scaleOrientation vector must have 4 elements.");
memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation));
continue;
}
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
CX3DImporter_NodeElement* ne;
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
}
else
{
ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
// at this place new group mode created and made current, so we can name it.
if(!def.empty()) NodeElement_Cur->ID = def;
//
// also set values specific to this type of group
//
// calculate tranformation matrix
aiMatrix4x4::Translation(translation, matr);// T
aiMatrix4x4::Translation(center, tmatr);// C
matr *= tmatr;
aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr);// R
matr *= tmatr;
aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// SR
matr *= tmatr;
aiMatrix4x4::Scaling(scale, tmatr);// S
matr *= tmatr;
aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// -SR
matr *= tmatr;
aiMatrix4x4::Translation(-center, tmatr);// -C
matr *= tmatr;
// and assign it
((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Transformation = matr;
// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
// for empty element exit from node in that place
if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
}// if(!use.empty()) else
}
void X3DImporter::ParseNode_Grouping_TransformEnd()
{
ParseHelper_Node_Exit();// go up in scene graph
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,249 @@
/// \file X3DImporter_Light.cpp
/// \brief Parsing data from nodes of "Lighting" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <DirectionalLight
// DEF="" ID
// USE="" IDREF
// ambientIntensity="0" SFFloat [inputOutput]
// color="1 1 1" SFColor [inputOutput]
// direction="0 0 -1" SFVec3f [inputOutput]
// global="false" SFBool [inputOutput]
// intensity="1" SFFloat [inputOutput]
// on="true" SFBool [inputOutput]
// />
void X3DImporter::ParseNode_Lighting_DirectionalLight()
{
std::string def, use;
float ambientIntensity = 0;
aiColor3D color(1, 1, 1);
aiVector3D direction(0, 0, -1);
bool global = false;
float intensity = 1;
bool on = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne);
}
else
{
if(on)
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur);
if(!def.empty())
ne->ID = def;
else
ne->ID = "DirectionalLight_" + std::to_string((size_t)ne);// make random name
((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
((CX3DImporter_NodeElement_Light*)ne)->Color = color;
((CX3DImporter_NodeElement_Light*)ne)->Direction = direction;
((CX3DImporter_NodeElement_Light*)ne)->Global = global;
((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
// Assimp want a node with name similar to a light. "Why? I don't no." )
ParseHelper_Group_Begin(false);
NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
ParseHelper_Node_Exit();
// check for child nodes
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "DirectionalLight");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(on)
}// if(!use.empty()) else
}
// <PointLight
// DEF="" ID
// USE="" IDREF
// ambientIntensity="0" SFFloat [inputOutput]
// attenuation="1 0 0" SFVec3f [inputOutput]
// color="1 1 1" SFColor [inputOutput]
// global="true" SFBool [inputOutput]
// intensity="1" SFFloat [inputOutput]
// location="0 0 0" SFVec3f [inputOutput]
// on="true" SFBool [inputOutput]
// radius="100" SFFloat [inputOutput]
// />
void X3DImporter::ParseNode_Lighting_PointLight()
{
std::string def, use;
float ambientIntensity = 0;
aiVector3D attenuation(1, 0, 0);
aiColor3D color(1, 1, 1);
bool global = true;
float intensity = 1;
aiVector3D location(0, 0, 0);
bool on = true;
float radius = 100;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne);
}
else
{
if(on)
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_PointLight, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
((CX3DImporter_NodeElement_Light*)ne)->Attenuation = attenuation;
((CX3DImporter_NodeElement_Light*)ne)->Color = color;
((CX3DImporter_NodeElement_Light*)ne)->Global = global;
((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
((CX3DImporter_NodeElement_Light*)ne)->Location = location;
((CX3DImporter_NodeElement_Light*)ne)->Radius = radius;
// Assimp want a node with name similar to a light. "Why? I don't no." )
ParseHelper_Group_Begin(false);
// make random name
if(ne->ID.empty()) ne->ID = "PointLight_" + std::to_string((size_t)ne);
NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
ParseHelper_Node_Exit();
// check for child nodes
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "PointLight");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(on)
}// if(!use.empty()) else
}
// <SpotLight
// DEF="" ID
// USE="" IDREF
// ambientIntensity="0" SFFloat [inputOutput]
// attenuation="1 0 0" SFVec3f [inputOutput]
// beamWidth="0.7854" SFFloat [inputOutput]
// color="1 1 1" SFColor [inputOutput]
// cutOffAngle="1.570796" SFFloat [inputOutput]
// direction="0 0 -1" SFVec3f [inputOutput]
// global="true" SFBool [inputOutput]
// intensity="1" SFFloat [inputOutput]
// location="0 0 0" SFVec3f [inputOutput]
// on="true" SFBool [inputOutput]
// radius="100" SFFloat [inputOutput]
// />
void X3DImporter::ParseNode_Lighting_SpotLight()
{
std::string def, use;
float ambientIntensity = 0;
aiVector3D attenuation(1, 0, 0);
float beamWidth = 0.7854f;
aiColor3D color(1, 1, 1);
float cutOffAngle = 1.570796f;
aiVector3D direction(0, 0, -1);
bool global = true;
float intensity = 1;
aiVector3D location(0, 0, 0);
bool on = true;
float radius = 100;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne);
}
else
{
if(on)
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_SpotLight, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
if(beamWidth > cutOffAngle) beamWidth = cutOffAngle;
((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
((CX3DImporter_NodeElement_Light*)ne)->Attenuation = attenuation;
((CX3DImporter_NodeElement_Light*)ne)->BeamWidth = beamWidth;
((CX3DImporter_NodeElement_Light*)ne)->Color = color;
((CX3DImporter_NodeElement_Light*)ne)->CutOffAngle = cutOffAngle;
((CX3DImporter_NodeElement_Light*)ne)->Direction = direction;
((CX3DImporter_NodeElement_Light*)ne)->Global = global;
((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
((CX3DImporter_NodeElement_Light*)ne)->Location = location;
((CX3DImporter_NodeElement_Light*)ne)->Radius = radius;
// Assimp want a node with name similar to a light. "Why? I don't no." )
ParseHelper_Group_Begin(false);
// make random name
if(ne->ID.empty()) ne->ID = "SpotLight_" + std::to_string((size_t)ne);
NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
ParseHelper_Node_Exit();
// check for child nodes
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "SpotLight");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(on)
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,154 @@
/// \file X3DImporter_Macro.hpp
/// \brief Useful macrodefines.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED
#define X3DIMPORTER_MACRO_HPP_INCLUDED
/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE)
/// Used for regular checking while attribute "USE" is defined.
/// \param [in] pDEF - string holding "DEF" value.
/// \param [in] pUSE - string holding "USE" value.
/// \param [in] pType - type of element to find.
/// \param [out] pNE - pointer to found node element.
#define MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pType, pNE) \
do { \
XML_CheckNode_MustBeEmpty(); \
if(!pDEF.empty()) Throw_DEF_And_USE(); \
if(!FindNodeElement(pUSE, CX3DImporter_NodeElement::pType, &pNE)) Throw_USE_NotFound(pUSE); \
\
NodeElement_Cur->Child.push_back(pNE);/* add found object as child to current element */ \
} while(false)
/// \def MACRO_ATTRREAD_LOOPBEG
/// Begin of loop that read attributes values.
#define MACRO_ATTRREAD_LOOPBEG \
for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
{ \
std::string an(mReader->getAttributeName(idx));
/// \def MACRO_ATTRREAD_LOOPEND
/// End of loop that read attributes values.
#define MACRO_ATTRREAD_LOOPEND \
Throw_IncorrectAttr(an); \
}
/// \def MACRO_ATTRREAD_CHECK_REF
/// Check curent attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
/// "continue" will called.
/// \param [in] pAttrName - attribute name.
/// \param [out] pVarName - output variable name.
/// \param [in] pFunction - function which read attribute value and write it to pVarName.
#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
if(an == pAttrName) \
{ \
pFunction(idx, pVarName); \
continue; \
}
/// \def MACRO_ATTRREAD_CHECK_RET
/// Check curent attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
/// If result was read then "continue" will called.
/// \param [in] pAttrName - attribute name.
/// \param [out] pVarName - output variable name.
/// \param [in] pFunction - function which read attribute value and write it to pVarName.
#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
if(an == pAttrName) \
{ \
pVarName = pFunction(idx); \
continue; \
}
/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET
/// Compact variant for checking "USE" and "DEF". Also skip bbox attributes: "bboxCenter", "bboxSize".
/// If result was read then "continue" will called.
/// \param [out] pDEF_Var - output variable name for "DEF" value.
/// \param [out] pUSE_Var - output variable name for "USE" value.
#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pDEF_Var, pUSE_Var) \
MACRO_ATTRREAD_CHECK_RET("DEF", pDEF_Var, mReader->getAttributeValue); \
MACRO_ATTRREAD_CHECK_RET("USE", pUSE_Var, mReader->getAttributeValue); \
if(an == "bboxCenter") continue; \
if(an == "bboxSize") continue; \
if(an == "containerField") continue; \
do {} while(false)
/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
/// Begin of loop of parsing child nodes. Do not add ';' at end.
/// \param [in] pNodeName - current node name.
#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
do { \
bool close_found = false; \
\
while(mReader->read()) \
{ \
if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
{
/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
/// End of loop of parsing child nodes.
/// \param [in] pNodeName - current node name.
#define MACRO_NODECHECK_LOOPEND(pNodeName) \
}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
{ \
if(XML_CheckNode_NameEqual(pNodeName)) \
{ \
close_found = true; \
\
break; \
} \
}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
}/* while(mReader->read()) */ \
\
if(!close_found) Throw_CloseNotFound(pNodeName); \
\
} while(false)
#define MACRO_NODECHECK_METADATA(pNodeName) \
MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
/* and childs must be metadata nodes */ \
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported(pNodeName); \
MACRO_NODECHECK_LOOPEND(pNodeName)
/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4)
/// Add points as quad. Means that pP1..pP4 set in CCW order.
#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \
do { \
if(pCCW) \
{ \
pOut.push_back(pIn[pP1]); \
pOut.push_back(pIn[pP2]); \
pOut.push_back(pIn[pP3]); \
pOut.push_back(pIn[pP4]); \
} \
else \
{ \
pOut.push_back(pIn[pP4]); \
pOut.push_back(pIn[pP3]); \
pOut.push_back(pIn[pP2]); \
pOut.push_back(pIn[pP1]); \
} \
} while(false)
/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4)
/// Add points as quad. Means that pP1..pP4 set in CCW order.
#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \
do { \
if(pCCW) \
{ \
pOut.push_back(pP1); \
pOut.push_back(pP2); \
pOut.push_back(pP3); \
pOut.push_back(pP4); \
} \
else \
{ \
pOut.push_back(pP4); \
pOut.push_back(pP3); \
pOut.push_back(pP2); \
pOut.push_back(pP1); \
} \
} while(false)
#endif // X3DIMPORTER_MACRO_HPP_INCLUDED

View File

@ -0,0 +1,236 @@
/// \file X3DImporter_Metadata.cpp
/// \brief Parsing data from nodes of "Metadata" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName)
/// Find element by "USE" or create new one.
/// \param [in] pDEF_Var - variable name with "DEF" value.
/// \param [in] pUSE_Var - variable name with "USE" value.
/// \param [in] pReference - variable name with "reference" value.
/// \param [in] pValue - variable name with "value" value.
/// \param [in, out] pNE - pointer to node element.
/// \param [in] pMetaClass - Class of node.
/// \param [in] pMetaName - Name of node.
/// \param [in] pType - type of element to find.
#define MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \
/* if "USE" defined then find already defined element. */ \
if(!pUSE_Var.empty()) \
{ \
MACRO_USE_CHECKANDAPPLY(pDEF_Var, pUSE_Var, pType, pNE); \
} \
else \
{ \
pNE = new pMetaClass(NodeElement_Cur); \
if(!pDEF_Var.empty()) pNE->ID = pDEF_Var; \
\
((pMetaClass*)pNE)->Reference = pReference; \
((pMetaClass*)pNE)->Value = pValue; \
/* also metadata node can contain childs */ \
if(!mReader->isEmptyElement()) \
ParseNode_Metadata(pNE, pMetaName);/* in that case node element will be added to child elements list of current node. */ \
else \
NodeElement_Cur->Child.push_back(pNE);/* else - add element to child list manualy */ \
\
NodeElement_List.push_back(pNE);/* add new element to elements list. */ \
}/* if(!pUSE_Var.empty()) else */ \
\
do {} while(false)
bool X3DImporter::ParseHelper_CheckRead_X3DMetadataObject()
{
if(XML_CheckNode_NameEqual("MetadataBoolean"))
ParseNode_MetadataBoolean();
else if(XML_CheckNode_NameEqual("MetadataDouble"))
ParseNode_MetadataDouble();
else if(XML_CheckNode_NameEqual("MetadataFloat"))
ParseNode_MetadataFloat();
else if(XML_CheckNode_NameEqual("MetadataInteger"))
ParseNode_MetadataInteger();
else if(XML_CheckNode_NameEqual("MetadataSet"))
ParseNode_MetadataSet();
else if(XML_CheckNode_NameEqual("MetadataString"))
ParseNode_MetadataString();
else
return false;
return true;
}
void X3DImporter::ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& pNodeName)
{
ParseHelper_Node_Enter(pParentElement);
MACRO_NODECHECK_METADATA(mReader->getNodeName());
ParseHelper_Node_Exit();
}
// <MetadataBoolean
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// value="" MFBool [inputOutput]
// />
void X3DImporter::ParseNode_MetadataBoolean()
{
std::string def, use;
std::string name, reference;
std::list<bool> value;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListB);
MACRO_ATTRREAD_LOOPEND;
MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaBoolean, "MetadataBoolean", ENET_MetaBoolean);
}
// <MetadataDouble
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// value="" MFDouble [inputOutput]
// />
void X3DImporter::ParseNode_MetadataDouble()
{
std::string def, use;
std::string name, reference;
std::list<double> value;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListD);
MACRO_ATTRREAD_LOOPEND;
MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaDouble, "MetadataDouble", ENET_MetaDouble);
}
// <MetadataFloat
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// value="" MFFloat [inputOutput]
// />
void X3DImporter::ParseNode_MetadataFloat()
{
std::string def, use;
std::string name, reference;
std::list<float> value;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListF);
MACRO_ATTRREAD_LOOPEND;
MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaFloat, "MetadataFloat", ENET_MetaFloat);
}
// <MetadataInteger
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// value="" MFInteger [inputOutput]
// />
void X3DImporter::ParseNode_MetadataInteger()
{
std::string def, use;
std::string name, reference;
std::list<int32_t> value;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_LOOPEND;
MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaInteger, "MetadataInteger", ENET_MetaInteger);
}
// <MetadataSet
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// />
void X3DImporter::ParseNode_MetadataSet()
{
std::string def, use;
std::string name, reference;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_MetaSet, ne);
}
else
{
ne = new CX3DImporter_NodeElement_MetaSet(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_MetaSet*)ne)->Reference = reference;
// also metadata node can contain childs
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "MetadataSet");
else
NodeElement_Cur->Child.push_back(ne);// made object as child to current element
NodeElement_List.push_back(ne);// add new element to elements list.
}// if(!use.empty()) else
}
// <MetadataString
// DEF="" ID
// USE="" IDREF
// name="" SFString [inputOutput]
// reference="" SFString [inputOutput]
// value="" MFString [inputOutput]
// />
void X3DImporter::ParseNode_MetadataString()
{
std::string def, use;
std::string name, reference;
std::list<std::string> value;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListS);
MACRO_ATTRREAD_LOOPEND;
MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaString, "MetadataString", ENET_MetaString);
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,70 @@
/// \file X3DImporter_Networking.cpp
/// \brief Parsing data from nodes of "Networking" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
// Header files, Assimp.
#include "DefaultIOSystem.h"
namespace Assimp
{
// <Inline
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// load="true" SFBool [inputOutput]
// url="" MFString [inputOutput]
// />
void X3DImporter::ParseNode_Networking_Inline()
{
std::string def, use;
bool load = true;
std::list<std::string> url;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("load", load, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("url", url, XML_ReadNode_GetAttrVal_AsListS);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
CX3DImporter_NodeElement* ne;
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
}
else
{
ParseHelper_Group_Begin(true);// create new grouping element and go deeper if node has children.
// at this place new group mode created and made current, so we can name it.
if(!def.empty()) NodeElement_Cur->ID = def;
if(load && (url.size() > 0))
{
DefaultIOSystem io_handler;
std::string full_path;
full_path = mFileDir + "/" + url.front();
// Attribute "url" can contain list of strings. But we need only one - first.
ParseFile(full_path, &io_handler);
}
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement()) ParseNode_Metadata(NodeElement_Cur, "Inline");
// exit from node in that place
ParseHelper_Node_Exit();
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,763 @@
/// \file X3DImporter_Node.hpp
/// \brief Elements of scene graph.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H
#define INCLUDED_AI_X3D_IMPORTER_NODE_H
// Header files, Assimp.
#include "assimp/scene.h"
#include "assimp/types.h"
// Header files, stdlib.
#include <list>
#include <string>
/// \class CX3DImporter_NodeElement
/// Base class for elements of nodes.
class CX3DImporter_NodeElement
{
/***********************************************/
/******************** Types ********************/
/***********************************************/
public:
/// \enum EType
/// Define what data type contain node element.
enum EType
{
ENET_Group, ///< Element has type "Group".
ENET_MetaBoolean, ///< Element has type "Metadata boolean".
ENET_MetaDouble, ///< Element has type "Metadata double".
ENET_MetaFloat, ///< Element has type "Metadata float".
ENET_MetaInteger, ///< Element has type "Metadata integer".
ENET_MetaSet, ///< Element has type "Metadata set".
ENET_MetaString, ///< Element has type "Metadata string".
ENET_Arc2D, ///< Element has type "Arc2D".
ENET_ArcClose2D, ///< Element has type "ArcClose2D".
ENET_Circle2D, ///< Element has type "Circle2D".
ENET_Disk2D, ///< Element has type "Disk2D".
ENET_Polyline2D, ///< Element has type "Polyline2D".
ENET_Polypoint2D, ///< Element has type "Polypoint2D".
ENET_Rectangle2D, ///< Element has type "Rectangle2D".
ENET_TriangleSet2D, ///< Element has type "TriangleSet2D".
ENET_Box, ///< Element has type "Box".
ENET_Cone, ///< Element has type "Cone".
ENET_Cylinder, ///< Element has type "Cylinder".
ENET_Sphere, ///< Element has type "Sphere".
ENET_ElevationGrid, ///< Element has type "ElevationGrid".
ENET_Extrusion, ///< Element has type "Extrusion".
ENET_Coordinate, ///< Element has type "Coordinate".
ENET_Normal, ///< Element has type "Normal".
ENET_TextureCoordinate, ///< Element has type "TextureCoordinate".
ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet".
ENET_IndexedLineSet, ///< Element has type "IndexedLineSet".
ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet".
ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet".
ENET_IndexedTriangleStripSet,///< Element has type "IndexedTriangleStripSet".
ENET_LineSet, ///< Element has type "LineSet".
ENET_PointSet, ///< Element has type "PointSet".
ENET_TriangleSet, ///< Element has type "TriangleSet".
ENET_TriangleFanSet, ///< Element has type "TriangleFanSet".
ENET_TriangleStripSet, ///< Element has type "TriangleStripSet".
ENET_Color, ///< Element has type "Color".
ENET_ColorRGBA, ///< Element has type "ColorRGBA".
ENET_Shape, ///< Element has type "Shape".
ENET_Appearance, ///< Element has type "Appearance".
ENET_Material, ///< Element has type "Material".
ENET_ImageTexture, ///< Element has type "ImageTexture".
ENET_TextureTransform, ///< Element has type "TextureTransform".
ENET_DirectionalLight, ///< Element has type "DirectionalLight".
ENET_PointLight, ///< Element has type "PointLight".
ENET_SpotLight, ///< Element has type "SpotLight".
ENET_Invalid ///< Element has invalid type and possible contain invalid data.
};
/***********************************************/
/****************** Constants ******************/
/***********************************************/
public:
const EType Type;
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
std::string ID;///< ID of the element. Can be empty. In X3D synonym for "ID" attribute.
CX3DImporter_NodeElement* Parent;///< Parrent element. If nullptr then this node is root.
std::list<CX3DImporter_NodeElement*> Child;///< Child elements.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement(const CX3DImporter_NodeElement& pNodeElement)
/// Disabled copy constructor.
CX3DImporter_NodeElement(const CX3DImporter_NodeElement& pNodeElement);
/// \fn CX3DImporter_NodeElement& operator=(const CX3DImporter_NodeElement& pNodeElement)
/// Disabled assign operator.
CX3DImporter_NodeElement& operator=(const CX3DImporter_NodeElement& pNodeElement);
/// \fn CX3DImporter_NodeElement()
/// Disabled default constructor.
CX3DImporter_NodeElement();
protected:
/// \fn CX3DImporter_NodeElement(const EType pType, CX3DImporter_NodeElement* pParent)
/// In constructor inheritor must set element type.
/// \param [in] pType - element type.
/// \param [in] pParent - parent element.
CX3DImporter_NodeElement(const EType pType, CX3DImporter_NodeElement* pParent)
: Type(pType), Parent(pParent)
{}
};// class IX3DImporter_NodeElement
/// \class CX3DImporter_NodeElement_Group
/// Class that define grouping node. Define transformation matrix for children.
/// Also can select which child will be kept and others are removed.
class CX3DImporter_NodeElement_Group : public CX3DImporter_NodeElement
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
aiMatrix4x4 Transformation;///< Transformation matrix.
/// \var bool Static
/// As you know node elements can use already defined node elements when attribute "USE" is defined.
/// Standard search when looking for an element in the whole scene graph, existing at this moment.
/// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static.
bool Static;
bool UseChoice;///< Flag: if true then use number from \ref Choice to choose what the child will be kept.
int32_t Choice;///< Number of the child which will be kept.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Group(const CX3DImporter_NodeElement_Group& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Group(const CX3DImporter_NodeElement_Group& pNode);
/// \fn CX3DImporter_NodeElement_Group& operator=(const CX3DImporter_NodeElement_Group& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Group& operator=(const CX3DImporter_NodeElement_Group& pNode);
/// \fn CX3DImporter_NodeElement_Group()
/// Disabled default constructor.
CX3DImporter_NodeElement_Group();
public:
/// \fn CX3DImporter_NodeElement_Group(CX3DImporter_NodeElement_Group* pParent, const bool pStatic = false)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pStatic - static node flag.
CX3DImporter_NodeElement_Group(CX3DImporter_NodeElement* pParent, const bool pStatic = false)
: CX3DImporter_NodeElement(ENET_Group, pParent), Static(pStatic), UseChoice(false)
{}
};// class CX3DImporter_NodeElement_Group
/// \class CX3DImporter_NodeElement_Meta
/// This struct describe metavalue.
class CX3DImporter_NodeElement_Meta : public CX3DImporter_NodeElement
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
std::string Name;///< Name of metadata object.
/// \var std::string Reference
/// If provided, it identifies the metadata standard or other specification that defines the name field. If the reference field is not provided or is
/// empty, the meaning of the name field is considered implicit to the characters in the string.
std::string Reference;
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Meta(const CX3DImporter_NodeElement_Meta& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Meta(const CX3DImporter_NodeElement_Meta& pNode);
/// \fn CX3DImporter_NodeElement_Meta& operator=(const CX3DImporter_NodeElement_Meta& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Meta& operator=(const CX3DImporter_NodeElement_Meta& pNode);
/// \fn CX3DImporter_NodeElement_Meta()
/// Disabled default constructor.
CX3DImporter_NodeElement_Meta();
public:
/// \fn CX3DImporter_NodeElement_Meta(const EType pType, CX3DImporter_NodeElement* pParent)
/// In constructor inheritor must set element type.
/// \param [in] pType - element type.
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Meta(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(pType, pParent)
{}
};// class CX3DImporter_NodeElement_Meta
/// \struct CX3DImporter_NodeElement_MetaBoolean
/// This struct describe metavalue of type boolean.
struct CX3DImporter_NodeElement_MetaBoolean : public CX3DImporter_NodeElement_Meta
{
std::list<bool> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaBoolean(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaBoolean(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaBoolean, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaBoolean
/// \struct CX3DImporter_NodeElement_MetaDouble
/// This struct describe metavalue of type double.
struct CX3DImporter_NodeElement_MetaDouble : public CX3DImporter_NodeElement_Meta
{
std::list<double> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaDouble(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaDouble(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaDouble, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaDouble
/// \struct CX3DImporter_NodeElement_MetaFloat
/// This struct describe metavalue of type float.
struct CX3DImporter_NodeElement_MetaFloat : public CX3DImporter_NodeElement_Meta
{
std::list<float> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaFloat(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaFloat(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaFloat, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaFloat
/// \struct CX3DImporter_NodeElement_MetaInteger
/// This struct describe metavalue of type integer.
struct CX3DImporter_NodeElement_MetaInteger : public CX3DImporter_NodeElement_Meta
{
std::list<int32_t> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaInteger(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaInteger(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaInteger, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaInteger
/// \struct CX3DImporter_NodeElement_MetaSet
/// This struct describe container for metaobjects.
struct CX3DImporter_NodeElement_MetaSet : public CX3DImporter_NodeElement_Meta
{
std::list<CX3DImporter_NodeElement_Meta> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaSet(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaSet(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaSet, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaSet
/// \struct CX3DImporter_NodeElement_MetaString
/// This struct describe metavalue of type string.
struct CX3DImporter_NodeElement_MetaString : public CX3DImporter_NodeElement_Meta
{
std::list<std::string> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_MetaString(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_MetaString(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Meta(ENET_MetaString, pParent)
{}
};// struct CX3DImporter_NodeElement_MetaString
/// \struct CX3DImporter_NodeElement_Color
/// This struct hold <Color> value.
struct CX3DImporter_NodeElement_Color : public CX3DImporter_NodeElement
{
std::list<aiColor3D> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_Color(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Color(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Color, pParent)
{}
};// struct CX3DImporter_NodeElement_Color
/// \struct CX3DImporter_NodeElement_ColorRGBA
/// This struct hold <ColorRGBA> value.
struct CX3DImporter_NodeElement_ColorRGBA : public CX3DImporter_NodeElement
{
std::list<aiColor4D> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_ColorRGBA(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_ColorRGBA(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_ColorRGBA, pParent)
{}
};// struct CX3DImporter_NodeElement_ColorRGBA
/// \struct CX3DImporter_NodeElement_Coordinate
/// This struct hold <Coordinate> value.
struct CX3DImporter_NodeElement_Coordinate : public CX3DImporter_NodeElement
{
std::list<aiVector3D> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_Coordinate(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Coordinate(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Coordinate, pParent)
{}
};// struct CX3DImporter_NodeElement_Coordinate
/// \struct CX3DImporter_NodeElement_Normal
/// This struct hold <Normal> value.
struct CX3DImporter_NodeElement_Normal : public CX3DImporter_NodeElement
{
std::list<aiVector3D> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_Normal(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Normal(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Normal, pParent)
{}
};// struct CX3DImporter_NodeElement_Normal
/// \struct CX3DImporter_NodeElement_TextureCoordinate
/// This struct hold <TextureCoordinate> value.
struct CX3DImporter_NodeElement_TextureCoordinate : public CX3DImporter_NodeElement
{
std::list<aiVector2D> Value;///< Stored value.
/// \fn CX3DImporter_NodeElement_TextureCoordinate(CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_TextureCoordinate(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_TextureCoordinate, pParent)
{}
};// struct CX3DImporter_NodeElement_TextureCoordinate
/// \class CX3DImporter_NodeElement_Geometry2D
/// Two-dimensional figure.
class CX3DImporter_NodeElement_Geometry2D : public CX3DImporter_NodeElement
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
std::list<aiVector3D> Vertices;///< Vertices list.
size_t NumIndices;///< Number of indices in one face.
bool Solid;///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Geometry2D(const CX3DImporter_NodeElement_Geometry2D& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Geometry2D(const CX3DImporter_NodeElement_Geometry2D& pNode);
/// \fn CX3DImporter_NodeElement_Geometry2D& operator=(const CX3DImporter_NodeElement_Geometry2D& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Geometry2D& operator=(const CX3DImporter_NodeElement_Geometry2D& pNode);
public:
/// \fn CX3DImporter_NodeElement_Geometry2D(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_Geometry2D(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(pType, pParent), Solid(true)
{}
};// class CX3DImporter_NodeElement_Geometry2D
/// \class CX3DImporter_NodeElement_Geometry3D
/// Three-dimensional body.
class CX3DImporter_NodeElement_Geometry3D : public CX3DImporter_NodeElement
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
std::list<aiVector3D> Vertices;///< Vertices list.
size_t NumIndices;///< Number of indices in one face.
bool Solid;///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Geometry3D(const CX3DImporter_NodeElement_Geometry3D& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Geometry3D(const CX3DImporter_NodeElement_Geometry3D& pNode);
/// \fn CX3DImporter_NodeElement_Geometry3D& operator=(const CX3DImporter_NodeElement_Geometry3D& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Geometry3D& operator=(const CX3DImporter_NodeElement_Geometry3D& pNode);
public:
/// \fn CX3DImporter_NodeElement_Geometry3D(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_Geometry3D(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(pType, pParent), Solid(true)
{}
};// class CX3DImporter_NodeElement_Geometry3D
/// \class CX3DImporter_NodeElement_ElevationGrid
/// Uniform rectangular grid of varying height.
class CX3DImporter_NodeElement_ElevationGrid : public CX3DImporter_NodeElement_Geometry3D
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
/// \var CreaseAngle
/// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
/// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
float CreaseAngle;
std::list<int32_t> CoordIdx;///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_ElevationGrid(const CX3DImporter_NodeElement_ElevationGrid& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_ElevationGrid(const CX3DImporter_NodeElement_ElevationGrid& pNode);
/// \fn CX3DImporter_NodeElement_ElevationGrid& operator=(const CX3DImporter_NodeElement_ElevationGrid& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_ElevationGrid& operator=(const CX3DImporter_NodeElement_ElevationGrid& pNode);
public:
/// \fn CX3DImporter_NodeElement_ElevationGrid(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_ElevationGrid(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
{}
};// class CX3DImporter_NodeElement_IndexedSet
/// \class CX3DImporter_NodeElement_IndexedSet
/// Shape with indexed vertices.
class CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
/// \var CCW
/// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
/// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
/// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
/// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
/// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
/// ccw field, results are undefined.
bool CCW;
std::list<int32_t> ColorIndex;///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
/// \var Convex
/// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself,
/// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results
/// even if the convex field is FALSE.
bool Convex;
std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
/// \var CreaseAngle
/// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
/// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
float CreaseAngle;
std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_IndexedSet(const CX3DImporter_NodeElement_IndexedSet& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_IndexedSet(const CX3DImporter_NodeElement_IndexedSet& pNode);
/// \fn CX3DImporter_NodeElement_IndexedSet& operator=(const CX3DImporter_NodeElement_IndexedSet& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_IndexedSet& operator=(const CX3DImporter_NodeElement_IndexedSet& pNode);
public:
/// \fn CX3DImporter_NodeElement_IndexedSet(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_IndexedSet(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
{}
};// class CX3DImporter_NodeElement_IndexedSet
/// \class CX3DImporter_NodeElement_Set
/// Shape with set of vertices.
class CX3DImporter_NodeElement_Set : public CX3DImporter_NodeElement_Geometry3D
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
/// \var CCW
/// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
/// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
/// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
/// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
/// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
/// ccw field, results are undefined.
bool CCW;
bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
std::list<int32_t> VertexCount;///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Set(const CX3DImporter_NodeElement_Set& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Set(const CX3DImporter_NodeElement_Set& pNode);
/// \fn CX3DImporter_NodeElement_Set& operator=(const CX3DImporter_NodeElement_Set& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Set& operator=(const CX3DImporter_NodeElement_Set& pNode);
public:
/// \fn CX3DImporter_NodeElement_Set(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_Set(const EType pType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
{}
};// class CX3DImporter_NodeElement_Set
/// \struct CX3DImporter_NodeElement_Shape
/// This struct hold <Shape> value.
struct CX3DImporter_NodeElement_Shape : public CX3DImporter_NodeElement
{
/// \fn CX3DImporter_NodeElement_Shape(CX3DImporter_NodeElement_Shape* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Shape(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Shape, pParent)
{}
};// struct CX3DImporter_NodeElement_Shape
/// \struct CX3DImporter_NodeElement_Appearance
/// This struct hold <Appearance> value.
struct CX3DImporter_NodeElement_Appearance : public CX3DImporter_NodeElement
{
/// \fn CX3DImporter_NodeElement_Appearance(CX3DImporter_NodeElement_Appearance* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_Appearance(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Appearance, pParent)
{}
};// struct CX3DImporter_NodeElement_Appearance
/// \class CX3DImporter_NodeElement_Material
/// Material.
class CX3DImporter_NodeElement_Material : public CX3DImporter_NodeElement
{
/***********************************************/
/****************** Variables ******************/
/***********************************************/
public:
float AmbientIntensity;///< Specifies how much ambient light from light sources this surface shall reflect.
aiColor3D DiffuseColor;///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source.
aiColor3D EmissiveColor;///< Models "glowing" objects. This can be useful for displaying pre-lit models.
float Shininess;///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
aiColor3D SpecularColor;///< The specularColor and shininess fields determine the specular highlights.
float Transparency;///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque.
/***********************************************/
/****************** Functions ******************/
/***********************************************/
private:
/// \fn CX3DImporter_NodeElement_Material(const CX3DImporter_NodeElement_Material& pNode)
/// Disabled copy constructor.
CX3DImporter_NodeElement_Material(const CX3DImporter_NodeElement_Material& pNode);
/// \fn CX3DImporter_NodeElement_Material& operator=(const CX3DImporter_NodeElement_Material& pNode)
/// Disabled assign operator.
CX3DImporter_NodeElement_Material& operator=(const CX3DImporter_NodeElement_Material& pNode);
public:
/// \fn CX3DImporter_NodeElement_Material(const EType pType, CX3DImporter_NodeElement* pParent)
/// Constructor.
/// \param [in] pParent - pointer to parent node.
/// \param [in] pType - type of geometry object.
CX3DImporter_NodeElement_Material(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_Material, pParent)
{}
};// class CX3DImporter_NodeElement_Material
/// \struct CX3DImporter_NodeElement_ImageTexture
/// This struct hold <ImageTexture> value.
struct CX3DImporter_NodeElement_ImageTexture : public CX3DImporter_NodeElement
{
/// \var RepeatS
/// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated
/// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are
/// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field.
bool RepeatS;
bool RepeatT;///< See \ref RepeatS.
std::string URL;///< URL of the texture.
/// \fn CX3DImporter_NodeElement_ImageTexture(CX3DImporter_NodeElement_ImageTexture* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_ImageTexture(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_ImageTexture, pParent)
{}
};// struct CX3DImporter_NodeElement_ImageTexture
/// \struct CX3DImporter_NodeElement_TextureTransform
/// This struct hold <TextureTransform> value.
struct CX3DImporter_NodeElement_TextureTransform : public CX3DImporter_NodeElement
{
aiVector2D Center;///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
float Rotation;///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
aiVector2D Scale;///< Specifies a scaling factor in S and T of the texture coordinates about the center point.
aiVector2D Translation;///< Specifies a translation of the texture coordinates.
/// \fn CX3DImporter_NodeElement_TextureTransform(CX3DImporter_NodeElement_TextureTransform* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
CX3DImporter_NodeElement_TextureTransform(CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(ENET_TextureTransform, pParent)
{}
};// struct CX3DImporter_NodeElement_TextureTransform
/// \struct CX3DImporter_NodeElement_Light
/// This struct hold <TextureTransform> value.
struct CX3DImporter_NodeElement_Light : public CX3DImporter_NodeElement
{
float AmbientIntensity;///< Specifies the intensity of the ambient emission from the light.
aiColor3D Color;///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value.
aiVector3D Direction;///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system.
/// \var Global
/// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence.
/// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light.
bool Global;
float Intensity;///< Specifies the brightness of the direct emission from the light.
/// \var Attenuation
/// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor
/// is: "1 / max(attenuation[0] + attenuation[1] × r + attenuation[2] × r2, 1)", where r is the distance from the light to the surface being illuminated.
aiVector3D Attenuation;
aiVector3D Location;///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin.
float Radius;///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source.
float BeamWidth;///< Specifies an inner solid angle in which the light source emits light at uniform full intensity.
float CutOffAngle;///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
/// \fn CX3DImporter_NodeElement_Light(EType pLightType, CX3DImporter_NodeElement* pParent)
/// Constructor
/// \param [in] pParent - pointer to parent node.
/// \param [in] pLightType - type of the light source.
CX3DImporter_NodeElement_Light(EType pLightType, CX3DImporter_NodeElement* pParent)
: CX3DImporter_NodeElement(pLightType, pParent)
{}
};// struct CX3DImporter_NodeElement_Light
#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H

View File

@ -0,0 +1,773 @@
/// \file X3DImporter_Postprocess.cpp
/// \brief Convert built scenegraph and objects to Assimp scenegraph.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
// Header files, Assimp.
#include "StandardShapes.h"
// Header files, stdlib.
#include <algorithm>
#include <iterator>
#include <string>
namespace Assimp
{
aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const
{
CX3DImporter_NodeElement* cur_node;
std::list<aiMatrix4x4> matr;
aiMatrix4x4 out_matr;
// starting walk from current element to root
cur_node = NodeElement_Cur;
if(cur_node != nullptr)
{
do
{
// if cur_node is group then store group transformation matrix in list.
if(cur_node->Type == CX3DImporter_NodeElement::ENET_Group) matr.push_back(((CX3DImporter_NodeElement_Group*)cur_node)->Transformation);
cur_node = cur_node->Parent;
} while(cur_node != nullptr);
}
// multiplicate all matrices in reverse order
for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); rit++) out_matr = out_matr * (*rit);
return out_matr;
}
void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
{
// walk thru childs and find for metadata.
for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
{
if(((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) ||
((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) ||
((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaString))
{
pList.push_back(*el_it);
}
else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaSet)
{
PostprocessHelper_CollectMetadata(**el_it, pList);
}
}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
}
bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const
{
if((pType == CX3DImporter_NodeElement::ENET_MetaBoolean) || (pType == CX3DImporter_NodeElement::ENET_MetaDouble) ||
(pType == CX3DImporter_NodeElement::ENET_MetaFloat) || (pType == CX3DImporter_NodeElement::ENET_MetaInteger) ||
(pType == CX3DImporter_NodeElement::ENET_MetaString) || (pType == CX3DImporter_NodeElement::ENET_MetaSet))
{
return true;
}
else
{
return false;
}
}
bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const
{
if((pType == CX3DImporter_NodeElement::ENET_Arc2D) || (pType == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
(pType == CX3DImporter_NodeElement::ENET_Box) || (pType == CX3DImporter_NodeElement::ENET_Circle2D) ||
(pType == CX3DImporter_NodeElement::ENET_Cone) || (pType == CX3DImporter_NodeElement::ENET_Cylinder) ||
(pType == CX3DImporter_NodeElement::ENET_Disk2D) || (pType == CX3DImporter_NodeElement::ENET_ElevationGrid) ||
(pType == CX3DImporter_NodeElement::ENET_Extrusion) || (pType == CX3DImporter_NodeElement::ENET_IndexedFaceSet) ||
(pType == CX3DImporter_NodeElement::ENET_IndexedLineSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
(pType == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet) ||
(pType == CX3DImporter_NodeElement::ENET_PointSet) || (pType == CX3DImporter_NodeElement::ENET_LineSet) ||
(pType == CX3DImporter_NodeElement::ENET_Polyline2D) || (pType == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
(pType == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pType == CX3DImporter_NodeElement::ENET_Sphere) ||
(pType == CX3DImporter_NodeElement::ENET_TriangleFanSet) || (pType == CX3DImporter_NodeElement::ENET_TriangleSet) ||
(pType == CX3DImporter_NodeElement::ENET_TriangleSet2D) || (pType == CX3DImporter_NodeElement::ENET_TriangleStripSet))
{
return true;
}
else
{
return false;
}
}
void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const
{
const CX3DImporter_NodeElement_Light& ne = *((CX3DImporter_NodeElement_Light*)&pNodeElement);
aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent();
aiLight* new_light = new aiLight;
new_light->mName = ne.ID;
new_light->mColorAmbient = ne.Color * ne.AmbientIntensity;
new_light->mColorDiffuse = ne.Color * ne.Intensity;
new_light->mColorSpecular = ne.Color * ne.Intensity;
switch(pNodeElement.Type)
{
case CX3DImporter_NodeElement::ENET_DirectionalLight:
new_light->mType = aiLightSource_DIRECTIONAL;
new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
break;
case CX3DImporter_NodeElement::ENET_PointLight:
new_light->mType = aiLightSource_POINT;
new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
new_light->mAttenuationConstant = ne.Attenuation.x;
new_light->mAttenuationLinear = ne.Attenuation.y;
new_light->mAttenuationQuadratic = ne.Attenuation.z;
break;
case CX3DImporter_NodeElement::ENET_SpotLight:
new_light->mType = aiLightSource_SPOT;
new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
new_light->mAttenuationConstant = ne.Attenuation.x;
new_light->mAttenuationLinear = ne.Attenuation.y;
new_light->mAttenuationQuadratic = ne.Attenuation.z;
new_light->mAngleInnerCone = ne.BeamWidth;
new_light->mAngleOuterCone = ne.CutOffAngle;
break;
default:
throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + std::to_string(pNodeElement.Type) + ".");
}
pSceneLightList.push_back(new_light);
}
void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const
{
// check argument
if(pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr.");
if(*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr.");
*pMaterial = new aiMaterial;
aiMaterial& taimat = **pMaterial;// creating alias for convenience.
// at this point pNodeElement point to <Appearance> node. Walk thru childs and add all stored data.
for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
{
if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
{
aiColor3D tcol3;
float tvalf;
CX3DImporter_NodeElement_Material& tnemat = *((CX3DImporter_NodeElement_Material*)*el_it);
tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity;
taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT);
taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE);
taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR);
tvalf = 1;
taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH);
taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS);
tvalf = 1.0f - tnemat.Transparency;
taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY);
}// if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
{
CX3DImporter_NodeElement_ImageTexture& tnetex = *((CX3DImporter_NodeElement_ImageTexture*)*el_it);
aiString url_str(tnetex.URL.c_str());
int mode = aiTextureOp_Multiply;
taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0));
taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
{
aiUVTransform trans;
CX3DImporter_NodeElement_TextureTransform& tnetextr = *((CX3DImporter_NodeElement_TextureTransform*)*el_it);
trans.mTranslation = tnetextr.Translation - tnetextr.Center;
trans.mScaling = tnetextr.Scale;
trans.mRotation = tnetextr.Rotation;
taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
}
void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const
{
// check argument
if(pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr.");
if(*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr.");
/************************************************************************************************************************************/
/************************************************************ Geometry2D ************************************************************/
/************************************************************************************************************************************/
if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D))
{
CX3DImporter_NodeElement_Geometry2D& tnemesh = *((CX3DImporter_NodeElement_Geometry2D*)&pNodeElement);// create alias for convenience
std::vector<aiVector3D> tarr;
tarr.reserve(tnemesh.Vertices.size());
for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
*pMesh = StandardShapes::MakeMesh(tarr, tnemesh.NumIndices);// create mesh from vertices using Assimp help.
return;// mesh is build, nothing to do anymore.
}
/************************************************************************************************************************************/
/************************************************************ Geometry3D ************************************************************/
/************************************************************************************************************************************/
//
// Predefined figures
//
if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere))
{
CX3DImporter_NodeElement_Geometry3D& tnemesh = *((CX3DImporter_NodeElement_Geometry3D*)&pNodeElement);// create alias for convenience
std::vector<aiVector3D> tarr;
tarr.reserve(tnemesh.Vertices.size());
for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
*pMesh = StandardShapes::MakeMesh(tarr, tnemesh.NumIndices);// create mesh from vertices using Assimp help.
return;// mesh is build, nothing to do anymore.
}
//
// Parametric figures
//
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
{
CX3DImporter_NodeElement_ElevationGrid& tnemesh = *((CX3DImporter_NodeElement_ElevationGrid*)&pNodeElement);// create alias for convenience
// at first create mesh from existing vertices.
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices);
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value, tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
//
// Indexed primitives sets
//
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
{
CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
{
CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
{
CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
IndexedTriangleStripSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
{
CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices);
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
//
// Primitives sets
//
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
{
CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
std::vector<aiVector3D> vec_copy;
vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
{
vec_copy.push_back(*it);
}
*pMesh = StandardShapes::MakeMesh(vec_copy, 1);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
{
CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
{
CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
{
CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
std::vector<aiVector3D> vec_copy;
vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
{
vec_copy.push_back(*it);
}
*pMesh = StandardShapes::MakeMesh(vec_copy, 3);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
{
CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
// at first search for <Coordinate> node and create mesh.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{
*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
}
}
// copy additional information from children
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
{
if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
{} // skip because already read when mesh created.
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
tnemesh.NormalPerVertex);
else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
else
throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + std::to_string((*ch_it)->Type) + ".");
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
return;// mesh is build, nothing to do anymore.
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + std::to_string(pNodeElement.Type) + ".");
}
void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const
{
std::list<CX3DImporter_NodeElement*>::const_iterator chit_begin = pNodeElement.Child.begin();
std::list<CX3DImporter_NodeElement*>::const_iterator chit_end = pNodeElement.Child.end();
std::list<aiNode*> SceneNode_Child;
std::list<unsigned int> SceneNode_Mesh;
// At first read all metadata
Postprocess_CollectMetadata(pNodeElement, pSceneNode);
// check if we have deal with grouping node. Which can contain transformation or switch
if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
{
const CX3DImporter_NodeElement_Group& tne_group = *((CX3DImporter_NodeElement_Group*)&pNodeElement);// create alias for convenience
pSceneNode.mTransformation = tne_group.Transformation;
if(tne_group.UseChoice)
{
// If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
if((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Child.size()))
{
chit_begin = pNodeElement.Child.end();
chit_end = pNodeElement.Child.end();
}
else
{
for(size_t i = 0; i < (size_t)tne_group.Choice; i++) chit_begin++;// forward iterator to choosed node.
chit_end = chit_begin;
chit_end++;// point end iterator to next element after choosed.
}
}// if(tne_group.UseChoice)
}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
// Reserve memory for fast access and check children.
for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
{// in this loop we do not read metadata because it's already read at begin.
if((*it)->Type == CX3DImporter_NodeElement::ENET_Group)
{
// if child is group then create new node and do recursive call.
aiNode* new_node = new aiNode;
new_node->mName = (*it)->ID;
new_node->mParent = &pSceneNode;
SceneNode_Child.push_back(new_node);
Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList);
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_Shape)
{
// shape can contain only one geometry and one appearance nodes.
Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape*)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList);
}
else if(((*it)->Type == CX3DImporter_NodeElement::ENET_DirectionalLight) || ((*it)->Type == CX3DImporter_NodeElement::ENET_PointLight) ||
((*it)->Type == CX3DImporter_NodeElement::ENET_SpotLight))
{
Postprocess_BuildLight(*((CX3DImporter_NodeElement_Light*)*it), pSceneLightList);
}
else if(!PostprocessHelper_ElementIsMetadata((*it)->Type))// skip metadata
{
throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + std::to_string((*it)->Type) + ".");
}
}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
// copy data about children and meshes to aiNode.
if(SceneNode_Child.size() > 0)
{
std::list<aiNode*>::const_iterator it = SceneNode_Child.begin();
pSceneNode.mNumChildren = SceneNode_Child.size();
pSceneNode.mChildren = new aiNode*[pSceneNode.mNumChildren];
for(size_t i = 0; i < pSceneNode.mNumChildren; i++) pSceneNode.mChildren[i] = *it++;
}
if(SceneNode_Mesh.size() > 0)
{
std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();
pSceneNode.mNumMeshes = SceneNode_Mesh.size();
pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *it++;
}
// that's all. return to previous deals
}
void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd,
std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const
{
aiMaterial* tmat = nullptr;
aiMesh* tmesh = nullptr;
CX3DImporter_NodeElement::EType mesh_type = CX3DImporter_NodeElement::ENET_Invalid;
unsigned int mat_ind = 0;
for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
{
if(PostprocessHelper_ElementIsMesh((*it)->Type))
{
Postprocess_BuildMesh(**it, &tmesh);
if(tmesh != nullptr)
{
// if mesh successfully built then add data about it to arrays
pNodeMeshInd.push_back(pSceneMeshList.size());
pSceneMeshList.push_back(tmesh);
// keep mesh type. Need above for texture coordinate generation.
mesh_type = (*it)->Type;
}
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_Appearance)
{
Postprocess_BuildMaterial(**it, &tmat);
if(tmat != nullptr)
{
// if material successfully built then add data about it to array
mat_ind = pSceneMaterialList.size();
pSceneMaterialList.push_back(tmat);
}
}
}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
// associate read material with read mesh.
if((tmesh != nullptr) && (tmat != nullptr))
{
tmesh->mMaterialIndex = mat_ind;
// Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates.
if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
{
int32_t tm;
aiVector3D tvec3;
switch(mesh_type)
{
case CX3DImporter_NodeElement::ENET_Box:
tm = aiTextureMapping_BOX;
break;
case CX3DImporter_NodeElement::ENET_Cone:
case CX3DImporter_NodeElement::ENET_Cylinder:
tm = aiTextureMapping_CYLINDER;
break;
case CX3DImporter_NodeElement::ENET_Sphere:
tm = aiTextureMapping_SPHERE;
break;
default:
tm = aiTextureMapping_PLANE;
break;
}// switch(mesh_type)
tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
}// if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
}// if((tmesh != nullptr) && (tmat != nullptr))
}
void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode) const
{
std::list<CX3DImporter_NodeElement*> meta_list;
size_t meta_idx;
PostprocessHelper_CollectMetadata(pNodeElement, meta_list);// find metadata in current node element.
if(meta_list.size() > 0)
{
if(pSceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
// copy collected metadata to output node.
pSceneNode.mMetaData = new aiMetadata();
pSceneNode.mMetaData->mNumProperties = meta_list.size();
pSceneNode.mMetaData->mKeys = new aiString[pSceneNode.mMetaData->mNumProperties];
pSceneNode.mMetaData->mValues = new aiMetadataEntry[pSceneNode.mMetaData->mNumProperties];
meta_idx = 0;
for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++, meta_idx++)
{
CX3DImporter_NodeElement_Meta* cur_meta = (CX3DImporter_NodeElement_Meta*)*it;
// due to limitations we can add only first element of value list.
// Add an element according to its type.
if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean)
{
if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.begin()));
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble)
{
// at this case also converting double to float.
if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble*)cur_meta)->Value.begin()));
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat)
{
if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat*)cur_meta)->Value.begin()));
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger)
{
if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger*)cur_meta)->Value.begin()));
}
else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString)
{
if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, ((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.begin()->data());
}
else
{
throw DeadlyImportError("Postprocess. Unknown metadata type.");
}// if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else
}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
}// if(meta_list.size() > 0)
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,937 @@
/// \file X3DImporter_Rendering.cpp
/// \brief Parsing data from nodes of "Rendering" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <Color
// DEF="" ID
// USE="" IDREF
// color="" MFColor [inputOutput]
// />
void X3DImporter::ParseNode_Rendering_Color()
{
std::string use, def;
std::list<aiColor3D> color;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsListCol3f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Color, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Color(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Color*)ne)->Value = color;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Color");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <ColorRGBA
// DEF="" ID
// USE="" IDREF
// color="" MFColorRGBA [inputOutput]
// />
void X3DImporter::ParseNode_Rendering_ColorRGBA()
{
std::string use, def;
std::list<aiColor4D> color;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsListCol4f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_ColorRGBA, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_ColorRGBA(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_ColorRGBA*)ne)->Value = color;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "ColorRGBA");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Coordinate
// DEF="" ID
// USE="" IDREF
// point="" MFVec3f [inputOutput]
// />
void X3DImporter::ParseNode_Rendering_Coordinate()
{
std::string use, def;
std::list<aiVector3D> point;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec3f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Coordinate, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Coordinate(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Coordinate*)ne)->Value = point;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Coordinate");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <IndexedLineSet
// DEF="" ID
// USE="" IDREF
// colorIndex="" MFInt32 [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// coordIndex="" MFInt32 [initializeOnly]
// >
// <!-- ColorCoordinateContentModel -->
// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </IndexedLineSet>
void X3DImporter::ParseNode_Rendering_IndexedLineSet()
{
std::string use, def;
std::list<int32_t> colorIndex;
bool colorPerVertex = true;
std::list<int32_t> coordIndex;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedLineSet, ne);
}
else
{
// check data
if((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3)))
throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedLineSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
ne_alias.ColorIndex = colorIndex;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.CoordIndex = coordIndex;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("IndexedLineSet");
// check for Color and Coordinate nodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedLineSet");
MACRO_NODECHECK_LOOPEND("IndexedLineSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <IndexedTriangleFanSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// index="" MFInt32 [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </IndexedTriangleFanSet>
void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
std::list<int32_t> index;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleFanSet, ne);
}
else
{
// check data
if(index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.CoordIndex = index;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleFanSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleFanSet");
MACRO_NODECHECK_LOOPEND("IndexedTriangleFanSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <IndexedTriangleSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// index="" MFInt32 [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </IndexedTriangleSet>
void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
std::list<int32_t> index;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleSet, ne);
}
else
{
// check data
if(index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.CoordIndex = index;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleSet");
MACRO_NODECHECK_LOOPEND("IndexedTriangleSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <IndexedTriangleStripSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// index="" MFInt32 [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </IndexedTriangleStripSet>
void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
std::list<int32_t> index;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleStripSet, ne);
}
else
{
// check data
if(index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.CoordIndex = index;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleStripSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleStripSet");
MACRO_NODECHECK_LOOPEND("IndexedTriangleStripSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <LineSet
// DEF="" ID
// USE="" IDREF
// vertexCount="" MFInt32 [initializeOnly]
// >
// <!-- ColorCoordinateContentModel -->
// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </LineSet>
void X3DImporter::ParseNode_Rendering_LineSet()
{
std::string use, def;
std::list<int32_t> vertexCount;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("vertexCount", vertexCount, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_LineSet, ne);
}
else
{
// check data
if(vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_LineSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
ne_alias.VertexCount = vertexCount;
// create CoordIdx
size_t coord_num = 0;
ne_alias.CoordIndex.clear();
for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
{
if(*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
for(int32_t i = 0; i < *vc_it; i++) ne_alias.CoordIndex.push_back(coord_num++);// add vertices indices
ne_alias.CoordIndex.push_back(-1);// add face delimiter.
}
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("LineSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("LineSet");
MACRO_NODECHECK_LOOPEND("LineSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <PointSet
// DEF="" ID
// USE="" IDREF
// >
// <!-- ColorCoordinateContentModel -->
// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </PointSet>
void X3DImporter::ParseNode_Rendering_PointSet()
{
std::string use, def;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointSet, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_PointSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("PointSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("PointSet");
MACRO_NODECHECK_LOOPEND("PointSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TriangleFanSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// fanCount="" MFInt32 [inputOutput]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </TriangleFanSet>
void X3DImporter::ParseNode_Rendering_TriangleFanSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
std::list<int32_t> fanCount;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("fanCount", fanCount, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleFanSet, ne);
}
else
{
// check data
if(fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_TriangleFanSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.VertexCount = fanCount;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// create CoordIdx
size_t coord_num_first, coord_num_prev;
ne_alias.CoordIndex.clear();
// assign indices for first triangle
coord_num_first = 0;
coord_num_prev = 1;
for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
{
if(*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
for(int32_t vc = 2; vc < *vc_it; vc++)
{
if(ccw)
{
// 2 1
// 0
ne_alias.CoordIndex.push_back(coord_num_first);// first vertex is a center and always is [0].
ne_alias.CoordIndex.push_back(coord_num_prev++);
ne_alias.CoordIndex.push_back(coord_num_prev);
}
else
{
// 1 2
// 0
ne_alias.CoordIndex.push_back(coord_num_first);// first vertex is a center and always is [0].
ne_alias.CoordIndex.push_back(coord_num_prev + 1);
ne_alias.CoordIndex.push_back(coord_num_prev++);
}// if(ccw) else
ne_alias.CoordIndex.push_back(-1);// add face delimiter.
}// for(int32_t vc = 2; vc < *vc_it; vc++)
coord_num_prev++;// that index will be center of next fan
coord_num_first = coord_num_prev++;// forward to next point - second point of fan
}// for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("TriangleFanSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleFanSet");
MACRO_NODECHECK_LOOPEND("TriangleFanSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TriangleSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </TriangleSet>
void X3DImporter::ParseNode_Rendering_TriangleSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_TriangleSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("TriangleSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleSet");
MACRO_NODECHECK_LOOPEND("TriangleSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TriangleStripSet
// DEF="" ID
// USE="" IDREF
// ccw="true" SFBool [initializeOnly]
// colorPerVertex="true" SFBool [initializeOnly]
// normalPerVertex="true" SFBool [initializeOnly]
// solid="true" SFBool [initializeOnly]
// stripCount="" MFInt32 [inputOutput]
// >
// <!-- ComposedGeometryContentModel -->
// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
// </TriangleStripSet>
void X3DImporter::ParseNode_Rendering_TriangleStripSet()
{
std::string use, def;
bool ccw = true;
bool colorPerVertex = true;
std::list<int32_t> stripCount;
bool normalPerVertex = true;
bool solid = true;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("stripCount", stripCount, XML_ReadNode_GetAttrVal_AsListI32);
MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleStripSet, ne);
}
else
{
// check data
if(stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute.");
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_TriangleStripSet, NodeElement_Cur);
if(!def.empty()) ne->ID = def;
CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
ne_alias.CCW = ccw;
ne_alias.ColorPerVertex = colorPerVertex;
ne_alias.VertexCount = stripCount;
ne_alias.NormalPerVertex = normalPerVertex;
ne_alias.Solid = solid;
// create CoordIdx
size_t coord_num0, coord_num1, coord_num2;// indices of current triangle
bool odd_tri;// sequence of current triangle
size_t coord_num_sb;// index of first point of strip
ne_alias.CoordIndex.clear();
coord_num_sb = 0;
for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
{
if(*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
// set initial values for first triangle
coord_num0 = coord_num_sb;
coord_num1 = coord_num_sb + 1;
coord_num2 = coord_num_sb + 2;
odd_tri = true;
for(int32_t vc = 2; vc < *vc_it; vc++)
{
if(ccw)
{
// 0 2
// 1
ne_alias.CoordIndex.push_back(coord_num0);
ne_alias.CoordIndex.push_back(coord_num1);
ne_alias.CoordIndex.push_back(coord_num2);
}
else
{
// 0 1
// 2
ne_alias.CoordIndex.push_back(coord_num0);
ne_alias.CoordIndex.push_back(coord_num2);
ne_alias.CoordIndex.push_back(coord_num1);
}// if(ccw) else
ne_alias.CoordIndex.push_back(-1);// add face delimiter.
// prepare values for next triangle
if(odd_tri)
{
coord_num0 = coord_num2;
coord_num2++;
}
else
{
coord_num1 = coord_num2;
coord_num2 = coord_num1 + 1;
}
odd_tri = !odd_tri;
coord_num_sb = coord_num2;// that index will be start of next strip
}// for(int32_t vc = 2; vc < *vc_it; vc++)
}// for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("TriangleStripSet");
// check for X3DComposedGeometryNodes
if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleStripSet");
MACRO_NODECHECK_LOOPEND("TriangleStripSet");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Normal
// DEF="" ID
// USE="" IDREF
// vector="" MFVec3f [inputOutput]
// />
void X3DImporter::ParseNode_Rendering_Normal()
{
std::string use, def;
std::list<aiVector3D> vector;
CX3DImporter_NodeElement* ne;
LogInfo("TRACE: scene rendering Normal b");
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("vector", vector, XML_ReadNode_GetAttrVal_AsListVec3f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Normal, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Normal(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Normal*)ne)->Value = vector;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Normal");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
LogInfo("TRACE: scene rendering Normal e");
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,209 @@
/// \file X3DImporter_Shape.cpp
/// \brief Parsing data from nodes of "Shape" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <Shape
// DEF="" ID
// USE="" IDREF
// bboxCenter="0 0 0" SFVec3f [initializeOnly]
// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
// >
// <!-- ShapeChildContentModel -->
// "ShapeChildContentModel is the child-node content model corresponding to X3DShapeNode. ShapeChildContentModel can contain a single Appearance node and a
// single geometry node, in any order.
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model."
// </Shape>
// A Shape node is unlit if either of the following is true:
// The shape's appearance field is NULL (default).
// The material field in the Appearance node is NULL (default).
// NOTE Geometry nodes that represent lines or points do not support lighting.
void X3DImporter::ParseNode_Shape_Shape()
{
std::string use, def;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Shape, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Shape(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("Shape");
// check for appearance node
if(XML_CheckNode_NameEqual("Appearance")) { ParseNode_Shape_Appearance(); continue; }
// check for X3DGeometryNodes
if(XML_CheckNode_NameEqual("Arc2D")) { ParseNode_Geometry2D_Arc2D(); continue; }
if(XML_CheckNode_NameEqual("ArcClose2D")) { ParseNode_Geometry2D_ArcClose2D(); continue; }
if(XML_CheckNode_NameEqual("Circle2D")) { ParseNode_Geometry2D_Circle2D(); continue; }
if(XML_CheckNode_NameEqual("Disk2D")) { ParseNode_Geometry2D_Disk2D(); continue; }
if(XML_CheckNode_NameEqual("Polyline2D")) { ParseNode_Geometry2D_Polyline2D(); continue; }
if(XML_CheckNode_NameEqual("Polypoint2D")) { ParseNode_Geometry2D_Polypoint2D(); continue; }
if(XML_CheckNode_NameEqual("Rectangle2D")) { ParseNode_Geometry2D_Rectangle2D(); continue; }
if(XML_CheckNode_NameEqual("TriangleSet2D")) { ParseNode_Geometry2D_TriangleSet2D(); continue; }
if(XML_CheckNode_NameEqual("Box")) { ParseNode_Geometry3D_Box(); continue; }
if(XML_CheckNode_NameEqual("Cone")) { ParseNode_Geometry3D_Cone(); continue; }
if(XML_CheckNode_NameEqual("Cylinder")) { ParseNode_Geometry3D_Cylinder(); continue; }
if(XML_CheckNode_NameEqual("ElevationGrid")) { ParseNode_Geometry3D_ElevationGrid(); continue; }
if(XML_CheckNode_NameEqual("Extrusion")) { ParseNode_Geometry3D_Extrusion(); continue; }
if(XML_CheckNode_NameEqual("IndexedFaceSet")) { ParseNode_Geometry3D_IndexedFaceSet(); continue; }
if(XML_CheckNode_NameEqual("Sphere")) { ParseNode_Geometry3D_Sphere(); continue; }
if(XML_CheckNode_NameEqual("IndexedLineSet")) { ParseNode_Rendering_IndexedLineSet(); continue; }
if(XML_CheckNode_NameEqual("LineSet")) { ParseNode_Rendering_LineSet(); continue; }
if(XML_CheckNode_NameEqual("PointSet")) { ParseNode_Rendering_PointSet(); continue; }
if(XML_CheckNode_NameEqual("IndexedTriangleFanSet")) { ParseNode_Rendering_IndexedTriangleFanSet(); continue; }
if(XML_CheckNode_NameEqual("IndexedTriangleSet")) { ParseNode_Rendering_IndexedTriangleSet(); continue; }
if(XML_CheckNode_NameEqual("IndexedTriangleStripSet")) { ParseNode_Rendering_IndexedTriangleStripSet(); continue; }
if(XML_CheckNode_NameEqual("TriangleFanSet")) { ParseNode_Rendering_TriangleFanSet(); continue; }
if(XML_CheckNode_NameEqual("TriangleSet")) { ParseNode_Rendering_TriangleSet(); continue; }
if(XML_CheckNode_NameEqual("TriangleStripSet")) { ParseNode_Rendering_TriangleStripSet(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("Shape");
MACRO_NODECHECK_LOOPEND("Shape");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Appearance
// DEF="" ID
// USE="" IDREF
// >
// <!-- AppearanceChildContentModel -->
// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and
// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader,
// PackagedShader, ProgramShader).
// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model."
// </Appearance>
void X3DImporter::ParseNode_Shape_Appearance()
{
std::string use, def;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Appearance, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Appearance(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
// check for child nodes
if(!mReader->isEmptyElement())
{
ParseHelper_Node_Enter(ne);
MACRO_NODECHECK_LOOPBEGIN("Appearance");
if(XML_CheckNode_NameEqual("Material")) { ParseNode_Shape_Material(); continue; }
if(XML_CheckNode_NameEqual("ImageTexture")) { ParseNode_Texturing_ImageTexture(); continue; }
if(XML_CheckNode_NameEqual("TextureTransform")) { ParseNode_Texturing_TextureTransform(); continue; }
// check for X3DMetadataObject
if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("Appearance");
MACRO_NODECHECK_LOOPEND("Appearance");
ParseHelper_Node_Exit();
}// if(!mReader->isEmptyElement())
else
{
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
}
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <Material
// DEF="" ID
// USE="" IDREF
// ambientIntensity="0.2" SFFloat [inputOutput]
// diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
// emissiveColor="0 0 0" SFColor [inputOutput]
// shininess="0.2" SFFloat [inputOutput]
// specularColor="0 0 0" SFColor [inputOutput]
// transparency="0" SFFloat [inputOutput]
// />
void X3DImporter::ParseNode_Shape_Material()
{
std::string use, def;
float ambientIntensity = 0.2f;
float shininess = 0.2f;
float transparency = 0;
aiColor3D diffuseColor(0.8f, 0.8f, 0.8f);
aiColor3D emissiveColor(0, 0, 0);
aiColor3D specularColor(0, 0, 0);
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("shininess", shininess, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_RET("transparency", transparency, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("diffuseColor", diffuseColor, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_CHECK_REF("emissiveColor", emissiveColor, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_CHECK_REF("specularColor", specularColor, XML_ReadNode_GetAttrVal_AsCol3f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_Material, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_Material(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_Material*)ne)->AmbientIntensity = ambientIntensity;
((CX3DImporter_NodeElement_Material*)ne)->Shininess = shininess;
((CX3DImporter_NodeElement_Material*)ne)->Transparency = transparency;
((CX3DImporter_NodeElement_Material*)ne)->DiffuseColor = diffuseColor;
((CX3DImporter_NodeElement_Material*)ne)->EmissiveColor = emissiveColor;
((CX3DImporter_NodeElement_Material*)ne)->SpecularColor = specularColor;
// check for child nodes
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "Material");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -0,0 +1,156 @@
/// \file X3DImporter_Texturing.cpp
/// \brief Parsing data from nodes of "Texturing" set of X3D.
/// \date 2015-2016
/// \author smal.root@gmail.com
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
#include "X3DImporter.hpp"
#include "X3DImporter_Macro.hpp"
namespace Assimp
{
// <ImageTexture
// DEF="" ID
// USE="" IDREF
// repeatS="true" SFBool
// repeatT="true" SFBool
// url="" MFString
// />
// When the url field contains no values ([]), texturing is disabled.
void X3DImporter::ParseNode_Texturing_ImageTexture()
{
std::string use, def;
bool repeatS = true;
bool repeatT = true;
std::list<std::string> url;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_RET("repeatS", repeatS, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_RET("repeatT", repeatT, XML_ReadNode_GetAttrVal_AsBool);
MACRO_ATTRREAD_CHECK_REF("url", url, XML_ReadNode_GetAttrVal_AsListS);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_ImageTexture, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_ImageTexture(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatS = repeatS;
((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatT = repeatT;
// Attribute "url" can contain list of strings. But we need only one - first.
if(url.size() > 0)
((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = url.front();
else
((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = "";
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "ImageTexture");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TextureCoordinate
// DEF="" ID
// USE="" IDREF
// point="" MFVec3f [inputOutput]
// />
void X3DImporter::ParseNode_Texturing_TextureCoordinate()
{
std::string use, def;
std::list<aiVector2D> point;
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TextureCoordinate, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_TextureCoordinate(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_TextureCoordinate*)ne)->Value = point;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "TextureCoordinate");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
// <TextureTransform
// DEF="" ID
// USE="" IDREF
// center="0 0" SFVec2f [inputOutput]
// rotation="0" SFFloat [inputOutput]
// scale="1 1" SFVec2f [inputOutput]
// translation="0 0" SFVec2f [inputOutput]
// />
void X3DImporter::ParseNode_Texturing_TextureTransform()
{
std::string use, def;
aiVector2D center(0, 0);
float rotation = 0;
aiVector2D scale(1, 1);
aiVector2D translation(0, 0);
CX3DImporter_NodeElement* ne;
MACRO_ATTRREAD_LOOPBEG;
MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec2f);
MACRO_ATTRREAD_CHECK_RET("rotation", rotation, XML_ReadNode_GetAttrVal_AsFloat);
MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec2f);
MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec2f);
MACRO_ATTRREAD_LOOPEND;
// if "USE" defined then find already defined element.
if(!use.empty())
{
MACRO_USE_CHECKANDAPPLY(def, use, ENET_TextureTransform, ne);
}
else
{
// create and if needed - define new geometry object.
ne = new CX3DImporter_NodeElement_TextureTransform(NodeElement_Cur);
if(!def.empty()) ne->ID = def;
((CX3DImporter_NodeElement_TextureTransform*)ne)->Center = center;
((CX3DImporter_NodeElement_TextureTransform*)ne)->Rotation = rotation;
((CX3DImporter_NodeElement_TextureTransform*)ne)->Scale = scale;
((CX3DImporter_NodeElement_TextureTransform*)ne)->Translation = translation;
// check for X3DMetadataObject childs.
if(!mReader->isEmptyElement())
ParseNode_Metadata(ne, "TextureTransform");
else
NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
}// if(!use.empty()) else
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

View File

@ -719,6 +719,7 @@ inline void Material::Read(Value& material, Asset& r)
ReadMaterialProperty(r, *values, "diffuse", this->diffuse);
ReadMaterialProperty(r, *values, "specular", this->specular);
ReadMember(*values, "transparency", transparency);
ReadMember(*values, "shininess", shininess);
}

View File

@ -184,6 +184,9 @@ namespace glTF {
WriteColorOrTex(v, m.specular, "specular", w.mAl);
WriteColorOrTex(v, m.emission, "emission", w.mAl);
if (m.transparent)
v.AddMember("transparency", m.transparency, w.mAl);
v.AddMember("shininess", m.shininess, w.mAl);
}
obj.AddMember("values", v, w.mAl);

View File

@ -353,6 +353,8 @@ void glTFExporter::ExportMaterials()
GetMatColorOrTex(mat, m->specular, AI_MATKEY_COLOR_SPECULAR, aiTextureType_SPECULAR);
GetMatColorOrTex(mat, m->emission, AI_MATKEY_COLOR_EMISSIVE, aiTextureType_EMISSIVE);
m->transparent = mat->Get(AI_MATKEY_OPACITY, m->transparency) == aiReturn_SUCCESS && m->transparency != 1.0;
GetMatScalar(mat, m->shininess, AI_MATKEY_SHININESS);
}
}

View File

@ -1,4 +1,4 @@
/*
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
@ -266,6 +266,13 @@ struct aiNode
*/
#define AI_SCENE_FLAGS_TERRAIN 0x10
/**
* Specifies that the scene data can be shared between structures. For example:
* one vertex in few faces. \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT can not be
* used for this because \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT has internal
* meaning about postprocessing steps.
*/
#define AI_SCENE_FLAGS_ALLOW_SHARED 0x20
// -------------------------------------------------------------------------------
/** The root structure of the imported data.

View File

@ -59,6 +59,14 @@ extern "C" {
// --------------------------------------------------------------------------------
/** \def AI_EMBEDDED_TEXNAME_PREFIX
* \ref AI_MAKE_EMBEDDED_TEXNAME
*/
#ifndef AI_EMBEDDED_TEXNAME_PREFIX
# define AI_EMBEDDED_TEXNAME_PREFIX "*"
#endif
/** @def AI_MAKE_EMBEDDED_TEXNAME
* Used to build the reserved path name used by the material system to
* reference textures that are embedded into their corresponding
@ -66,7 +74,7 @@ extern "C" {
* (zero-based, in the aiScene::mTextures array)
*/
#if (!defined AI_MAKE_EMBEDDED_TEXNAME)
# define AI_MAKE_EMBEDDED_TEXNAME(_n_) "*" # _n_
# define AI_MAKE_EMBEDDED_TEXNAME(_n_) AI_EMBEDDED_TEXNAME_PREFIX # _n_
#endif
@ -139,10 +147,17 @@ struct aiTexture
unsigned int mHeight;
/** A hint from the loader to make it easier for applications
* to determine the type of embedded compressed textures.
* to determine the type of embedded textures.
*
* If mHeight != 0 this member is undefined. Otherwise it
* is set set to '\\0\\0\\0\\0' if the loader has no additional
* If mHeight != 0 this member is show how data is packed. Hint will consist of
* two parts: channel order and channel bitness (count of the bits for every
* color channel). For simple parsing by the viewer it's better to not omit
* absent color channel and just use 0 for bitness. For example:
* 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888";
* 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888";
* 2. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650";
* 3. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010";
* If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional
* information about the texture file format used OR the
* file extension of the format without a trailing dot. If there
* are multiple file extensions for a format, the shortest
@ -150,7 +165,7 @@ struct aiTexture
* E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case.
* The fourth character will always be '\\0'.
*/
char achFormatHint[4];
char achFormatHint[9];// 8 for string + 1 for terminator.
/** Data of the texture.
*
@ -172,7 +187,7 @@ struct aiTexture
//! @return true if the given string matches the format hint
bool CheckFormat(const char* s) const
{
return (0 == ::strncmp(achFormatHint,s,3));
return (0 == ::strncmp(achFormatHint, s, sizeof(achFormatHint)));
}
// Construction

View File

@ -164,7 +164,118 @@ std::list<aiMatrix4x4> mat_list;
void CGLView::ImportTextures(const QString& pScenePath)
{
auto LoadTexture = [&](const QString& pFileName) -> bool ///TODO: IME texture mode, operation.
{
ILboolean success;
GLuint id_ogl_texture;// OpenGL texture ID.
if(!pFileName.startsWith(AI_EMBEDDED_TEXNAME_PREFIX))
{
ILuint id_image;// DevIL image ID.
QString basepath = pScenePath.left(pScenePath.lastIndexOf('/') + 1);// path with '/' at the end.
QString fileloc = (basepath + pFileName);
fileloc.replace('\\', "/");
ilGenImages(1, &id_image);// Generate DevIL image ID.
ilBindImage(id_image);
success = ilLoadImage(fileloc.toLocal8Bit());
if(!success)
{
LogError(QString("Couldn't load Image: %1").arg(fileloc));
return false;
}
// Convert every colour component into unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA.
success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
if(!success)
{
LogError("Couldn't convert image.");
return false;
}
glGenTextures(1, &id_ogl_texture);// Texture ID generation.
mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map
glBindTexture(GL_TEXTURE_2D, id_ogl_texture);// Binding of texture ID.
// Redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0,
ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());// Texture specification.
//Cleanup
ilDeleteImages(1, &id_image);// Because we have already copied image data into texture data we can release memory used by image.
}
else
{
struct SPixel_Description
{
const char* FormatHint;
const GLint Image_InternalFormat;
const GLint Pixel_Format;
};
constexpr SPixel_Description Pixel_Description[] = {
{"rgba8880", GL_RGB, GL_RGB},
{"rgba8888", GL_RGBA, GL_RGBA}
};
constexpr size_t Pixel_Description_Count = sizeof(Pixel_Description) / sizeof(SPixel_Description);
size_t idx_description;
// Get texture index.
bool ok;
size_t idx_texture = pFileName.right(strlen(AI_EMBEDDED_TEXNAME_PREFIX)).toULong(&ok);
if(!ok)
{
LogError("Can not get index of the embedded texture from path in material.");
return false;
}
// Create alias for conveniance.
const aiTexture& als = *mScene->mTextures[idx_texture];
if(als.mHeight == 0)// Compressed texture.
{
LogError("IME: compressed embedded textures are not implemented.");
}
else
{
ok = false;
for(size_t idx = 0; idx < Pixel_Description_Count; idx++)
{
if(als.CheckFormat(Pixel_Description[idx].FormatHint))
{
idx_description = idx;
ok = true;
break;
}
}
if(!ok)
{
LogError(QString("Unsupported format hint for embedded texture: [%1]").arg(als.achFormatHint));
return false;
}
glGenTextures(1, &id_ogl_texture);// Texture ID generation.
mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map
glBindTexture(GL_TEXTURE_2D, id_ogl_texture);// Binding of texture ID.
// Redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
// Texture specification.
glTexImage2D(GL_TEXTURE_2D, 0, Pixel_Description[idx_description].Image_InternalFormat, als.mWidth, als.mHeight, 0,
Pixel_Description[idx_description].Pixel_Format, GL_UNSIGNED_BYTE, (uint8_t*)als.pcData);
}// if(als.mHeight == 0) else
}// if(!filename.startsWith(AI_EMBEDDED_TEXNAME_PREFIX)) else
return true;
};// auto LoadTexture = [&](const aiString& pPath)
if(mScene == nullptr)
{
@ -183,12 +294,7 @@ ILboolean success;
ilInit();// Initialization of DevIL.
//
// Load embedded textures
//
if(mScene->HasTextures()) LogError("Support for meshes with embedded textures is not implemented.");
//
// Load textures from external files.
// Load textures.
//
// Get textures file names and number of textures.
for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++)
@ -200,10 +306,10 @@ ILboolean success;
{
if(mScene->mMaterials[idx_material]->GetTexture(aiTextureType_DIFFUSE, idx_texture, &path) != AI_SUCCESS) break;
mTexture_IDMap[path.data] = 0;// Fill map with invalid ID's.
LoadTexture(QString(path.C_Str()));
idx_texture++;
} while(true);
}// for(size_t idx_mat = 0; idx_mat < scene->mNumMaterials; idx_mat++)
}// for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++)
// Textures list is empty, exit.
if(mTexture_IDMap.size() == 0)
@ -212,71 +318,6 @@ ILboolean success;
return;
}
size_t num_textures = mTexture_IDMap.size();
ILuint* id_images = nullptr;// Array with DevIL image ID's.
GLuint* id_textures = nullptr;// Array with OpenGL textures ID's.
// Generate DevIL image IDs.
id_images = new ILuint[num_textures];
ilGenImages(num_textures, id_images);// Generation of 'num_textures' image names.
// Create and fill array with OpenGL texture ID's.
id_textures = new GLuint[num_textures];
///TODO: if can not load textures then will stay orphande texture ID's in OpenGL. Generate OpenGL ID's after successfull loading of image.
glGenTextures(num_textures, id_textures);// Texture ID's generation.
QMap<QString, GLuint>::iterator map_it = mTexture_IDMap.begin();// Get iterator
QString basepath = pScenePath.left(pScenePath.lastIndexOf('/') + 1);// path with '/' at the end.
for(size_t idx_texture = 0; idx_texture < num_textures; idx_texture++)
{
//save IL image ID
QString filename = map_it.key();// get filename
mTexture_IDMap[filename] = id_textures[idx_texture];// save texture ID for filename in map
map_it++;// next texture
ilBindImage(id_images[idx_texture]);// Binding of DevIL image name.
QString fileloc = basepath + filename; /* Loading of image */
fileloc.replace('\\', "/");
success = ilLoadImage(fileloc.toLocal8Bit());
if(!success)
{
LogError(QString("Couldn't load Image: %1").arg(fileloc));
goto it_for_err;
}
// Convert every colour component into unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA.
success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
if(!success)
{
LogError("Couldn't convert image.");
goto it_for_err;
}
glBindTexture(GL_TEXTURE_2D, id_textures[idx_texture]);// Binding of texture ID.
// Redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0,
ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());// Texture specification.
continue;
it_for_err:
LogError(QString("DevIL error: %1, [%2]").arg(ilGetError()).arg(ilGetString(ilGetError())));
mTexture_IDMap.remove(filename);
}// for(size_t idx_texture = 0; idx_texture < num_textures; i++)
// Because we have already copied image data into texture data we can release memory used by image.
ilDeleteImages(num_textures, id_images);
//Cleanup
delete [] id_images;
delete [] id_textures;
}
void CGLView::BBox_GetForNode(const aiNode& pNode, const aiMatrix4x4& pParent_TransformationMatrix, SBBox& pNodeBBox, bool& pFirstAssign)

View File

@ -156,6 +156,8 @@ private:
// Lighting
bool mLightingEnabled = false;///< If true then OpenGL lighting is enabled (glEnable(GL_LIGHTING)), if false - disabled.
// Textures
///TODO: map is goooood, but not for case when one image can be used in different materials with difference in: texture transformation, targeting of the
/// texture (ambient or emission, or even height map), texture properties.
QMap<QString, GLuint> mTexture_IDMap;///< Map image filenames to textures ID's.
/**********************************/