assimp/code/X3DImporter.cpp

1586 lines
54 KiB
C++

/// \file X3DImporter.cpp
/// \brief X3D-format files importer for Assimp: main algorithm implementation.
/// \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"
#include "fast_atof.h"
// Header files, stdlib.
#include <memory>
#include <string>
namespace Assimp
{
/// \var aiImporterDesc X3DImporter::Description
/// Conastant which hold importer description
const aiImporterDesc X3DImporter::Description = {
"Extensible 3D(X3D) Importer",
"smalcom",
"",
"See documentation in source code. Chapter: Limitations.",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
0,
0,
0,
0,
"x3d"
};
void X3DImporter::Clear()
{
NodeElement_Cur = nullptr;
// Delete all elements
if(NodeElement_List.size())
{
for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++) delete *it;
NodeElement_List.clear();
}
}
X3DImporter::~X3DImporter()
{
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 X3DImporter::FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
{
for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
{
if(((*it)->Type == pType) && ((*it)->ID == pID))
{
if(pElement != nullptr) *pElement = *it;
return true;
}
}// for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
return false;
}
bool X3DImporter::FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID,
const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
{
bool found = false;// flag: true - if requested element is found.
// Check if pStartNode - this is the element, we are looking for.
if((pStartNode->Type == pType) && (pStartNode->ID == pID))
{
found = true;
if(pElement != nullptr) *pElement = pStartNode;
goto fne_fn_end;
}// if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
// Check childs of pStartNode.
for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = pStartNode->Child.begin(); ch_it != pStartNode->Child.end(); ch_it++)
{
found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
if(found) break;
}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Child.begin(); ch_it != it->Child.end(); ch_it++)
fne_fn_end:
return found;
}
bool X3DImporter::FindNodeElement(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
{
CX3DImporter_NodeElement* tnd = NodeElement_Cur;// temporary pointer to node.
bool static_search = false;// flag: true if searching in static node.
// At first check if we have deal with static node. Go up thru parent nodes and check flag.
while(tnd != nullptr)
{
if(tnd->Type == CX3DImporter_NodeElement::ENET_Group)
{
if(((CX3DImporter_NodeElement_Group*)tnd)->Static)
{
static_search = true;// Flag found, stop walking up. Node with static flag will holded in tnd variable.
break;
}
}
tnd = tnd->Parent;// go up in graph.
}// while(tnd != nullptr)
// at now call appropriate search function.
if(static_search)
return FindNodeElement_FromNode(tnd, pID, pType, pElement);
else
return FindNodeElement_FromRoot(pID, pType, pElement);
}
/*********************************************************************************************************************************************/
/************************************************************ Functions: throw set ***********************************************************/
/*********************************************************************************************************************************************/
void X3DImporter::Throw_ArgOutOfRange(const std::string& pArgument)
{
throw DeadlyImportError("Argument value is out of range for: \"" + pArgument + "\".");
}
void X3DImporter::Throw_CloseNotFound(const std::string& pNode)
{
throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
}
void X3DImporter::Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
{
throw DeadlyImportError("In <" + std::string(mReader->getNodeName()) + "> failed to convert attribute value \"" + pAttrValue +
"\" from string to array of floats.");
}
void X3DImporter::Throw_DEF_And_USE()
{
throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + std::string(mReader->getNodeName()) + ">.");
}
void X3DImporter::Throw_IncorrectAttr(const std::string& pAttrName)
{
throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
}
void X3DImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
{
throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
}
void X3DImporter::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 X3DImporter::Throw_TagCountIncorrect(const std::string& pNode)
{
throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt.");
}
void X3DImporter::Throw_USE_NotFound(const std::string& pAttrValue)
{
throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + std::string(mReader->getNodeName()) + ">.");
}
/*********************************************************************************************************************************************/
/************************************************************* Functions: XML set ************************************************************/
/*********************************************************************************************************************************************/
void X3DImporter::XML_CheckNode_MustBeEmpty()
{
if(!mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must be empty.");
}
void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
{
const size_t Uns_Skip_Len = 189;
const char* Uns_Skip[Uns_Skip_Len] = {
// CAD geometry component
"CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
// Core
"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"
};
std::string nn(mReader->getNodeName());
bool found = false;
bool close_found = false;
for(size_t i = 0; i < Uns_Skip_Len; i++)
{
if(nn == Uns_Skip[i])
{
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;
}
}
}
}
casu_cres:
if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
if(close_found)
LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
else
Throw_CloseNotFound(nn);
}
bool X3DImporter::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 X3DImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
{
std::string val(mReader->getAttributeValue(pAttrIdx));
if(val == "false")
return false;
else if(val == "true")
return true;
else
throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
}
float X3DImporter::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;
}
int32_t X3DImporter::XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
{
return strtol10(mReader->getAttributeValue(pAttrIdx));
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
{
std::list<float> tlist;
std::list<float>::iterator it;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
it = tlist.begin();
pValue.r = *it++;
pValue.g = *it++;
pValue.b = *it;
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
{
std::list<float> tlist;
std::list<float>::iterator it;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
if(tlist.size() != 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
it = tlist.begin();
pValue.x = *it++;
pValue.y = *it;
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
{
std::list<float> tlist;
std::list<float>::iterator it;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
it = tlist.begin();
pValue.x = *it++;
pValue.y = *it++;
pValue.z = *it;
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue)
{
char* tok_str;
size_t tok_str_len;
// make copy of attribute value - string with list of bool values. Also all bool values is strings.
tok_str_len = strlen(mReader->getAttributeValue(pAttrIdx));
if(!tok_str_len) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
tok_str_len++;// take in account terminating '\0'.
tok_str = new char[tok_str_len];
strcpy(tok_str, mReader->getAttributeValue(pAttrIdx));
// change all spacebars to symbol '\0'. That is needed for parsing.
for(size_t i = 0; i < tok_str_len; i++)
{
if(tok_str[i] == ' ') tok_str[i] = 0;
}
// at now check what current token is
for(char *tok_cur = tok_str, *tok_end = (tok_str + tok_str_len); tok_cur < tok_end;)
{
if(strncmp(tok_cur, "true", 4) == 0)
{
pValue.push_back(true);
tok_cur += 5;// five, not four. Because '\0' must be skipped too.
}
else if(strncmp(tok_cur, "false", 5) == 0)
{
pValue.push_back(true);
tok_cur += 6;// six, not five. Because '\0' must be skipped too.
}
else
{
Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
}
}// for(char* tok_cur = tok_str, tok_end = (tok_str + tok_str_len); tok_cur < tok_end;)
// delete temporary string
delete [] tok_str;
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
{
std::list<bool> tlist;
XML_ReadNode_GetAttrVal_AsListB(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<bool>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
{
const char* tstr = mReader->getAttributeValue(pAttrIdx);
const char* tstr_end = tstr + strlen(tstr);
do
{
const char* ostr;
int32_t tval32;
tval32 = strtol10(tstr, &ostr);
if(ostr == tstr) break;
while((ostr < tstr_end) && (*ostr == ' ')) ostr++;// skip spaces between values.
tstr = ostr;
pValue.push_back(tval32);
} while(tstr < tstr_end);
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
{
std::list<int32_t> tlist;
XML_ReadNode_GetAttrVal_AsListI32(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<int32_t>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
{
std::string str_fixed;
// at first check string values like '.xxx'.
ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
if(!str_fixed.size()) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// and convert all values and place it in list.
const char* pstr = str_fixed.c_str();
const char* pstr_end = pstr + str_fixed.size();
do
{
float tvalf;
while((*pstr == ' ') && (pstr < pstr_end)) pstr++;// skip spaces between values.
if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
{
pstr = fast_atoreal_move(pstr, tvalf, false);
pValue.push_back(tvalf);
}
} while(pstr < pstr_end);
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
{
std::list<float> tlist;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<float>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
{
std::string str_fixed;
// at first check string values like '.xxx'.
ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
if(!str_fixed.size()) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// and convert all values and place it in list.
const char* pstr = str_fixed.c_str();
const char* pstr_end = pstr + str_fixed.size();
do
{
double tvald;
while((*pstr == ' ') && (pstr < pstr_end)) pstr++;// skip spaces between values.
if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
{
pstr = fast_atoreal_move(pstr, tvald, false);
pValue.push_back(tvald);
}
} while(pstr < pstr_end);
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
{
std::list<double> tlist;
XML_ReadNode_GetAttrVal_AsListD(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<double>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
{
std::list<float> tlist;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// copy data to array
for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
{
aiColor3D tcol;
tcol.r = *it++;
tcol.g = *it++;
tcol.b = *it++;
pValue.push_back(tcol);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
{
std::list<aiColor3D> tlist;
XML_ReadNode_GetAttrVal_AsListCol3f(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<aiColor3D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
{
std::list<float> tlist;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
if(tlist.size() % 4) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// copy data to array
for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
{
aiColor4D tcol;
tcol.r = *it++;
tcol.g = *it++;
tcol.b = *it++;
tcol.a = *it++;
pValue.push_back(tcol);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue)
{
std::list<aiColor4D> tlist;
XML_ReadNode_GetAttrVal_AsListCol4f(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<aiColor4D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
{
std::list<float> tlist;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
if(tlist.size() % 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// copy data to array
for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
{
aiVector2D tvec;
tvec.x = *it++;
tvec.y = *it++;
pValue.push_back(tvec);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue)
{
std::list<aiVector2D> tlist;
XML_ReadNode_GetAttrVal_AsListVec2f(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
{
std::list<float> tlist;
XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
// copy data to array
for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
{
aiVector3D tvec;
tvec.x = *it++;
tvec.y = *it++;
tvec.z = *it++;
pValue.push_back(tvec);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue)
{
std::list<aiVector3D> tlist;
XML_ReadNode_GetAttrVal_AsListVec3f(pAttrIdx, tlist);// read as list
// and copy to array
if(tlist.size() > 0)
{
pValue.reserve(tlist.size());
for(std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
}
}
void X3DImporter::XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue)
{
char* tok_str;
char* tok_str_end;
size_t tok_str_len;
// make copy of attribute value - strings list.
tok_str_len = strlen(mReader->getAttributeValue(pAttrIdx));
if(!tok_str_len) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
// get pointer to begin of value.
tok_str = const_cast<char*>(mReader->getAttributeValue(pAttrIdx));
tok_str_end = tok_str + tok_str_len;
// string list has following format: attr_name='"s1" "s2" "sn"'.
do
{
char* tbeg;
char* tend;
size_t tlen;
std::string tstr;
// find begin of string(element of string list): "sn".
tbeg = strstr(tok_str, "\"");
if(tbeg == nullptr) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
tbeg++;// forward pointer from '\"' symbol to next after it.
tok_str = tbeg;
// find end of string(element of string list): "sn".
tend = strstr(tok_str, "\"");
if(tend == nullptr) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
tok_str = tend + 1;
// create storage for new string
tlen = tend - tbeg;
tstr.resize(tlen);// reserve enough space and copy data
memcpy((void*)tstr.data(), tbeg, tlen);// not strcpy because end of copied string from tok_str has no terminator.
// and store string in output list.
pValue.push_back(tstr);
} while(tok_str < tok_str_end);
}
/*********************************************************************************************************************************************/
/****************************************************** Functions: geometry helper set ******************************************************/
/*********************************************************************************************************************************************/
aiVector3D X3DImporter::GeometryHelper_Make_Point2D(const float pAngle, const float pRadius)
{
return aiVector3D(pRadius * cosf(pAngle), pRadius * sinf(pAngle), 0);
}
void X3DImporter::GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments,
std::list<aiVector3D>& pVertices)
{
float angle_full, angle_step;
// check argument values ranges.
if((pStartAngle < -AI_MATH_TWO_PI_F) || (pStartAngle > AI_MATH_TWO_PI_F)) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pStartAngle");
if((pEndAngle < -AI_MATH_TWO_PI_F) || (pEndAngle > AI_MATH_TWO_PI_F)) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pEndAngle");
if(pRadius <= 0) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pRadius");
// calculate arc angle and check type of arc
angle_full = fabs(pEndAngle - pStartAngle);
if((angle_full > AI_MATH_TWO_PI_F) || (angle_full == 0.0f)) angle_full = AI_MATH_TWO_PI_F;
// calculate angle for one step - angle to next point of line.
angle_step = angle_full / (float)pNumSegments;
// make points
for(size_t pi = 0; pi <= pNumSegments; pi++)
{
float tangle;
tangle = pStartAngle + pi * angle_step;
pVertices.push_back(GeometryHelper_Make_Point2D(tangle, pRadius));
}// for(size_t pi = 0; pi <= pNumSegments; pi++)
// if we making full circle then add last vertex equal to first vertex
if(angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin());
}
void X3DImporter::GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine)
{
std::list<aiVector3D>::const_iterator pit = pPoint.begin();
std::list<aiVector3D>::const_iterator pit_last = pPoint.end();
pit_last--;
if(pPoint.size() < 2) Throw_ArgOutOfRange("GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2.");
// add first point of first line.
pLine.push_back(*pit++);
// add internal points
while(pit != pit_last)
{
pLine.push_back(*pit);// second point of previous line
pLine.push_back(*pit);// first point of next line
pit++;
}
// add last point of last line
pLine.push_back(*pit);
}
void X3DImporter::GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx)
{
std::list<int32_t>::const_iterator plit = pPolylineCoordIdx.begin();
while(plit != pPolylineCoordIdx.end())
{
// add first point of polyline
pLineCoordIdx.push_back(*plit++);
while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
{
std::list<int32_t>::const_iterator plit_next;
plit_next = plit, plit_next++;
pLineCoordIdx.push_back(*plit);// second point of previous line.
pLineCoordIdx.push_back(-1);// delimiter
if((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break;// current polyline is finished
pLineCoordIdx.push_back(*plit);// first point of next line.
plit = plit_next;
}// while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
}// while(plit != pPolylineCoordIdx.end())
}
#define MESH_RectParallelepiped_CREATE_VERT \
aiVector3D vert_set[8]; \
float x1, x2, y1, y2, z1, z2, hs; \
\
hs = pSize.x / 2, x1 = -hs, x2 = hs; \
hs = pSize.y / 2, y1 = -hs, y2 = hs; \
hs = pSize.z / 2, z1 = -hs, z2 = hs; \
vert_set[0].Set(x2, y1, z2); \
vert_set[1].Set(x2, y2, z2); \
vert_set[2].Set(x2, y2, z1); \
vert_set[3].Set(x2, y1, z1); \
vert_set[4].Set(x1, y1, z2); \
vert_set[5].Set(x1, y2, z2); \
vert_set[6].Set(x1, y2, z1); \
vert_set[7].Set(x1, y1, z1)
void X3DImporter::GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices)
{
MESH_RectParallelepiped_CREATE_VERT;
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0);// front
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5);// back
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4);// left
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1);// right
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4);// top
MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3);// bottom
}
#undef MESH_RectParallelepiped_CREATE_VERT
void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
{
std::list<int32_t> f_data(pCoordIdx);
std::vector<unsigned int> inds;
unsigned int prim_type = 0;
if(f_data.back() != (-1)) f_data.push_back(-1);
// reserve average size.
pFaces.reserve(f_data.size() / 3);
inds.reserve(4);
//PrintVectorSet("build. ci", pCoordIdx);
for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
{
// when face is got count how many indices in it.
if(*it == (-1))
{
aiFace tface;
size_t ts;
ts = inds.size();
switch(ts)
{
case 0: goto mg_m_err;
case 1: prim_type |= aiPrimitiveType_POINT; break;
case 2: prim_type |= aiPrimitiveType_LINE; break;
case 3: prim_type |= aiPrimitiveType_TRIANGLE; break;
default: prim_type |= aiPrimitiveType_POLYGON; break;
}
tface.mNumIndices = ts;
tface.mIndices = new unsigned int[ts];
memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int));
pFaces.push_back(tface);
inds.clear();
}// if(*it == (-1))
else
{
inds.push_back(*it);
}// if(*it == (-1)) else
}// for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
//PrintVectorSet("build. faces", pCoordIdx);
pPrimitiveTypes = prim_type;
return;
mg_m_err:
for(size_t i = 0, i_e = pFaces.size(); i < i_e; i++) delete [] pFaces.at(i).mIndices;
pFaces.clear();
}
void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
{
std::list<aiColor4D> tcol;
// create RGBA array from RGB.
for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
// call existing function for adding RGBA colors
MeshGeometry_AddColor(pMesh, tcol, pColorPerVertex);
}
void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
{
std::list<aiColor4D>::const_iterator col_it = pColors.begin();
if(pColorPerVertex)
{
if(pColors.size() < pMesh.mNumVertices)
{
throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + std::to_string(pColors.size()) + ") can not be less than Vertices count(" +
std::to_string(pMesh.mNumVertices) + ").");
}
// copy colors to mesh
pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mColors[0][i] = *col_it++;
}// if(pColorPerVertex)
else
{
if(pColors.size() < pMesh.mNumFaces)
{
throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + std::to_string(pColors.size()) + ") can not be less than Faces count(" +
std::to_string(pMesh.mNumFaces) + ").");
}
// copy colors to mesh
pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
{
// apply color to all vertices of face
for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mColors[0][pMesh.mFaces[fi].mIndices[vi]] = *col_it;
col_it++;
}
}// if(pColorPerVertex) else
}
void X3DImporter::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
{
std::list<aiColor4D> tcol;
// create RGBA array from RGB.
for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
// call existing function for adding RGBA colors
MeshGeometry_AddColor(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex);
}
void X3DImporter::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
{
std::vector<aiColor4D> col_tgt_arr;
std::list<aiColor4D> col_tgt_list;
std::vector<aiColor4D> col_arr_copy;
if(pCoordIdx.size() == 0) throw DeadlyImportError("MeshGeometry_AddColor2. pCoordIdx can not be empty.");
// copy list to array because we are need indexed access to colors.
col_arr_copy.reserve(pColors.size());
for(std::list<aiColor4D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) col_arr_copy.push_back(*it);
if(pColorPerVertex)
{
if(pColorIdx.size() > 0)
{
// check indices array count.
if(pColorIdx.size() < pCoordIdx.size())
{
throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + std::to_string(pColorIdx.size()) +
") can not be less than Coords inidces count(" + std::to_string(pCoordIdx.size()) + ").");
}
// create list with colors for every vertex.
col_tgt_arr.resize(pMesh.mNumVertices);
for(std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); colidx_it++, coordidx_it++)
{
if(*colidx_it == (-1)) continue;// skip faces delimiter
if((unsigned int)(*coordidx_it) > pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddColor2. Coordinate idx is out of range.");
if((unsigned int)*colidx_it > pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddColor2. Color idx is out of range.");
col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it];
}
}// if(pColorIdx.size() > 0)
else
{
// when color indices list is absent use CoordIdx.
// check indices array count.
if(pColors.size() < pMesh.mNumVertices)
{
throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + std::to_string(pColors.size()) + ") can not be less than Vertices count(" +
std::to_string(pMesh.mNumVertices) + ").");
}
// create list with colors for every vertex.
col_tgt_arr.resize(pMesh.mNumVertices);
for(size_t i = 0; i < pMesh.mNumVertices; i++) col_tgt_arr[i] = col_arr_copy[i];
}// if(pColorIdx.size() > 0) else
}// if(pColorPerVertex)
else
{
if(pColorIdx.size() > 0)
{
// check indices array count.
if(pColorIdx.size() < pMesh.mNumFaces)
{
throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + std::to_string(pColorIdx.size()) +
") can not be less than Faces count(" + std::to_string(pMesh.mNumFaces) + ").");
}
// create list with colors for every vertex using faces indices.
col_tgt_arr.resize(pMesh.mNumFaces);
std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin();
for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
{
if((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range.");
col_tgt_arr[fi] = col_arr_copy[*colidx_it++];
}
}// if(pColorIdx.size() > 0)
else
{
// when color indices list is absent use CoordIdx.
// check indices array count.
if(pColors.size() < pMesh.mNumFaces)
{
throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + std::to_string(pColors.size()) + ") can not be less than Faces count(" +
std::to_string(pMesh.mNumFaces) + ").");
}
// create list with colors for every vertex using faces indices.
col_tgt_arr.resize(pMesh.mNumFaces);
for(size_t fi = 0; fi < pMesh.mNumFaces; fi++) col_tgt_arr[fi] = col_arr_copy[fi];
}// if(pColorIdx.size() > 0) else
}// if(pColorPerVertex) else
// copy array to list for calling function that add colors.
for(std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); it++) col_tgt_list.push_back(*it);
// add prepared colors list to mesh.
MeshGeometry_AddColor(pMesh, col_tgt_list, pColorPerVertex);
}
void X3DImporter::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
{
std::vector<size_t> tind;
std::vector<aiVector3D> norm_arr_copy;
// copy list to array because we are need indexed access to normals.
norm_arr_copy.reserve(pNormals.size());
for(std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); it++) norm_arr_copy.push_back(*it);
if(pNormalPerVertex)
{
const std::list<int32_t>* srcidx;
if(pNormalIdx.size() > 0)
{
// check indices array count.
if(pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal.");
srcidx = &pNormalIdx;
}
else
{
srcidx = &pCoordIdx;
}
tind.reserve(srcidx->size());
for(std::list<int32_t>::const_iterator it = srcidx->begin(); it != srcidx->end(); it++)
{
if(*it != (-1)) tind.push_back(*it);
}
// copy normals to mesh
pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
for(size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++)
{
if(tind[i] >= norm_arr_copy.size())
throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + std::to_string(tind[i]) +
") is out of range. Normals count: " + std::to_string(norm_arr_copy.size()) + ".");
pMesh.mNormals[i] = norm_arr_copy[tind[i]];
}
}// if(pNormalPerVertex)
else
{
if(pNormalIdx.size() > 0)
{
if(pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count.");
std::list<int32_t>::const_iterator normidx_it = pNormalIdx.begin();
tind.reserve(pNormalIdx.size());
for(size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) tind.push_back(*normidx_it++);
}
else
{
tind.reserve(pMesh.mNumFaces);
for(size_t i = 0; i < pMesh.mNumFaces; i++) tind.push_back(i);
}
// copy normals to mesh
pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
{
aiVector3D tnorm;
tnorm = norm_arr_copy[tind[fi]];
for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm;
}
}// if(pNormalPerVertex) else
}
void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
{
std::list<aiVector3D>::const_iterator norm_it = pNormals.begin();
if(pNormalPerVertex)
{
if(pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal.");
// copy normals to mesh
pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mNormals[i] = *norm_it++;
}// if(pNormalPerVertex)
else
{
if(pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal.");
// copy normals to mesh
pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
{
// apply color to all vertices of face
for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it;
norm_it++;
}
}// if(pNormalPerVertex) else
}
void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
const std::list<aiVector2D>& pTexCoords) const
{
std::vector<aiVector3D> texcoord_arr_copy;
std::vector<aiFace> faces;
unsigned int prim_type;
// copy list to array because we are need indexed access to normals.
texcoord_arr_copy.reserve(pTexCoords.size());
for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++)
{
texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
}
if(pTexCoordIdx.size() > 0)
{
GeometryHelper_CoordIdxStr2FacesArr(pTexCoordIdx, faces, prim_type);
if(!faces.size()) throw DeadlyImportError("Failed to add texture coordinates to mesh, faces list is empty.");
if(faces.size() != pMesh.mNumFaces) throw DeadlyImportError("Texture coordinates faces count must be equal to mesh faces count.");
}
else
{
GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
}
pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
pMesh.mNumUVComponents[0] = 2;
for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
{
if(pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices)
throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + std::to_string(fi) + ".");
for(size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++)
{
size_t vert_idx = pMesh.mFaces[fi].mIndices[ii];
size_t tc_idx = faces.at(fi).mIndices[ii];
pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx);
}
}// for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
}
void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const
{
std::vector<aiVector3D> tc_arr_copy;
if(pTexCoords.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal.");
// copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus.
tc_arr_copy.reserve(pTexCoords.size());
for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++) tc_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
// copy texture coordinates to mesh
pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
pMesh.mNumUVComponents[0] = 2;
for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mTextureCoords[0][i] = tc_arr_copy[i];
}
aiMesh* X3DImporter::GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
{
aiMesh* tmesh;
std::vector<aiFace> faces;
unsigned int prim_type = 0;
size_t ts;
// create faces array from input string with vertices indices.
GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
if(!faces.size()) throw DeadlyImportError("Failed to create mesh, faces list is empty.");
//
// Create new mesh and copy geometry data.
//
tmesh = new aiMesh;
ts = faces.size();
// faces
tmesh->mFaces = new aiFace[ts];
tmesh->mNumFaces = ts;
for(size_t i = 0; i < ts; i++) tmesh->mFaces[i] = faces.at(i);
// vertices
std::list<aiVector3D>::const_iterator vit = pVertices.begin();
ts = pVertices.size();
tmesh->mVertices = new aiVector3D[ts];
tmesh->mNumVertices = ts;
for(size_t i = 0; i < ts; i++) tmesh->mVertices[i] = *vit++;
// set primitives type and return result.
tmesh->mPrimitiveTypes = prim_type;
return tmesh;
}
/*********************************************************************************************************************************************/
/************************************************************ Functions: parse set ***********************************************************/
/*********************************************************************************************************************************************/
void X3DImporter::ParseHelper_Group_Begin(const bool pStatic)
{
CX3DImporter_NodeElement_Group* new_group = new CX3DImporter_NodeElement_Group(NodeElement_Cur, pStatic);// create new node with current node as parent.
// if we are adding not the root element then add new element to current element child list.
if(NodeElement_Cur != nullptr) NodeElement_Cur->Child.push_back(new_group);
NodeElement_List.push_back(new_group);// it's a new element - add it to list.
NodeElement_Cur = new_group;// switch current element to new one.
}
void X3DImporter::ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode)
{
NodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
NodeElement_Cur = pNode;// switch current element to new one.
}
void X3DImporter::ParseHelper_Node_Exit()
{
// check if we can walk up.
if(NodeElement_Cur != nullptr) NodeElement_Cur = NodeElement_Cur->Parent;
}
void X3DImporter::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]);
}
}
}
void X3DImporter::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() == nullptr) throw DeadlyImportError("Failed to open X3D 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
ParseNode_Root();
delete mReader;
// restore old XMLreader
mReader = OldReader;
}
void X3DImporter::ParseNode_Root()
{
LogInfo("ParseNode_Root b");
// search for root tag <X3D>
if(!XML_SearchNode("X3D")) throw DeadlyImportError("Root node \"X3D\" not found.");
ParseHelper_Group_Begin();// create root node element.
// parse other contents
LogInfo("ParseNode_Root. read loop");
while(mReader->read())
{
if(mReader->getNodeType() != irr::io::EXN_ELEMENT) continue;
if(XML_CheckNode_NameEqual("head"))
ParseNode_Head();
else if(XML_CheckNode_NameEqual("Scene"))
ParseNode_Scene();
else
XML_CheckNode_SkipUnsupported("Root");
}
LogInfo("ParseNode_Root. end loop");
// exit from root node element.
ParseHelper_Node_Exit();
LogInfo("ParseNode_Root e");
}
void X3DImporter::ParseNode_Head()
{
bool close_found = false;// flag: true if close tag of node are found.
while(mReader->read())
{
if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if(XML_CheckNode_NameEqual("meta"))
{
XML_CheckNode_MustBeEmpty();
// adding metada from <head> as MetaString from <Scene>
CX3DImporter_NodeElement_MetaString* ms = new CX3DImporter_NodeElement_MetaString(NodeElement_Cur);
ms->Name = mReader->getAttributeValueSafe("name");
// name can not be empty
if(!ms->Name.empty())
{
ms->Value.push_back(mReader->getAttributeValueSafe("content"));
NodeElement_List.push_back(ms);
if(NodeElement_Cur != nullptr) NodeElement_Cur->Child.push_back(ms);
}
}// if(XML_CheckNode_NameEqual("meta"))
}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if(XML_CheckNode_NameEqual("head"))
{
close_found = true;
break;
}
}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
}// while(mReader->read())
if(!close_found) Throw_CloseNotFound("head");
}
void X3DImporter::ParseNode_Scene()
{
auto GroupCounter_Increase = [](size_t& pCounter, const char* pGroupName) -> void
{
pCounter++;
if(pCounter == 0) throw DeadlyImportError("Group counter overflow. Too much groups with type: " + std::string(pGroupName) + ".");
};
auto GroupCounter_Decrease = [&](size_t& pCounter, const char* pGroupName) -> void
{
if(pCounter == 0) Throw_TagCountIncorrect(pGroupName);
pCounter--;
};
const char* GroupName_Group = "Group";
const char* GroupName_StaticGroup = "StaticGroup";
const char* GroupName_Transform = "Transform";
const char* GroupName_Switch = "Switch";
bool close_found = false;
size_t counter_group = 0;
size_t counter_transform = 0;
size_t counter_switch = 0;
// while create static node? Because objects name used deeper in "USE" attribute can be equal to some meta in <head> node.
ParseHelper_Group_Begin(true);
while(mReader->read())
{
if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if(XML_CheckNode_NameEqual("Shape"))
{
ParseNode_Shape_Shape();
}
else if(XML_CheckNode_NameEqual(GroupName_Group))
{
GroupCounter_Increase(counter_group, GroupName_Group);
ParseNode_Grouping_Group();
// if node is empty then decrease group counter at this place.
if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_group, GroupName_Group);
}
else if(XML_CheckNode_NameEqual(GroupName_StaticGroup))
{
GroupCounter_Increase(counter_group, GroupName_StaticGroup);
ParseNode_Grouping_StaticGroup();
// if node is empty then decrease group counter at this place.
if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_group, GroupName_StaticGroup);
}
else if(XML_CheckNode_NameEqual(GroupName_Transform))
{
GroupCounter_Increase(counter_transform, GroupName_Transform);
ParseNode_Grouping_Transform();
// if node is empty then decrease group counter at this place.
if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_transform, GroupName_Transform);
}
else if(XML_CheckNode_NameEqual(GroupName_Switch))
{
GroupCounter_Increase(counter_switch, GroupName_Switch);
ParseNode_Grouping_Switch();
// if node is empty then decrease group counter at this place.
if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_switch, GroupName_Switch);
}
else if(XML_CheckNode_NameEqual("DirectionalLight"))
{
ParseNode_Lighting_DirectionalLight();
}
else if(XML_CheckNode_NameEqual("PointLight"))
{
ParseNode_Lighting_PointLight();
}
else if(XML_CheckNode_NameEqual("SpotLight"))
{
ParseNode_Lighting_SpotLight();
}
else if(XML_CheckNode_NameEqual("Inline"))
{
ParseNode_Networking_Inline();
}
else if(!ParseHelper_CheckRead_X3DMetadataObject())
{
XML_CheckNode_SkipUnsupported("Scene");
}
}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if(XML_CheckNode_NameEqual("Scene"))
{
close_found = true;
break;
}
else if(XML_CheckNode_NameEqual(GroupName_Group))
{
GroupCounter_Decrease(counter_group, GroupName_Group);
ParseNode_Grouping_GroupEnd();
}
else if(XML_CheckNode_NameEqual(GroupName_StaticGroup))
{
GroupCounter_Decrease(counter_group, GroupName_StaticGroup);
ParseNode_Grouping_StaticGroupEnd();
}
else if(XML_CheckNode_NameEqual(GroupName_Transform))
{
GroupCounter_Decrease(counter_transform, GroupName_Transform);
ParseNode_Grouping_TransformEnd();
}
else if(XML_CheckNode_NameEqual(GroupName_Switch))
{
GroupCounter_Decrease(counter_switch, GroupName_Switch);
ParseNode_Grouping_SwitchEnd();
}
}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
}// while(mReader->read())
ParseHelper_Node_Exit();
if(counter_group) Throw_TagCountIncorrect("Group");
if(counter_transform) Throw_TagCountIncorrect("Transform");
if(counter_switch) Throw_TagCountIncorrect("Switch");
if(!close_found) Throw_CloseNotFound("Scene");
}
/*********************************************************************************************************************************************/
/******************************************************** Functions: BaseImporter set ********************************************************/
/*********************************************************************************************************************************************/
bool X3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
{
const std::string extension = GetExtension(pFile);
if(extension == "x3d") return true;
if(!extension.length() || pCheckSig)
{
const char* tokens[] = { "DOCTYPE X3D PUBLIC", "http://www.web3d.org/specifications/x3d" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
}
return false;
}
void X3DImporter::GetExtensionList(std::set<std::string>& pExtensionList)
{
pExtensionList.insert("x3d");
}
const aiImporterDesc* X3DImporter::GetInfo () const
{
return &Description;
}
void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
Clear();// delete old graph.
mFileDir = DefaultIOSystem::absolutePath(pFile);
ParseFile(pFile, pIOHandler);
//
// Assimp use static arrays of objects for fast speed of rendering. That's good, but need some additional operations/
// We know that geometry objects(meshes) are stored in <Shape>, also in <Shape>-><Appearance> materials(in Assimp logical view)
// are stored. So at first we need to count how meshes and materials are stored in scene graph.
//
// 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 node element
NodeElement_Cur = NodeElement_List.front();
while(NodeElement_Cur->Parent != nullptr) NodeElement_Cur = NodeElement_Cur->Parent;
{// fill aiScene with objects.
std::list<aiMesh*> mesh_list;
std::list<aiMaterial*> mat_list;
std::list<aiLight*> light_list;
// create nodes tree
Postprocess_BuildNode(*NodeElement_Cur, *pScene->mRootNode, mesh_list, mat_list, light_list);
// copy needed data to scene
if(mesh_list.size() > 0)
{
std::list<aiMesh*>::const_iterator 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] = *it++;
}
if(mat_list.size() > 0)
{
std::list<aiMaterial*>::const_iterator it = mat_list.begin();
pScene->mNumMaterials = mat_list.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
for(size_t i = 0; i < pScene->mNumMaterials; i++) pScene->mMaterials[i] = *it++;
}
if(light_list.size() > 0)
{
std::list<aiLight*>::const_iterator it = light_list.begin();
pScene->mNumLights = light_list.size();
pScene->mLights = new aiLight*[pScene->mNumLights];
for(size_t i = 0; i < pScene->mNumLights; i++) pScene->mLights[i] = *it++;
}
}// END: fill aiScene with objects.
///TODO: IME optimize tree
}
}// namespace Assimp
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER