/// \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 #include 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::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::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::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::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::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 tlist; std::list::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 tlist; std::list::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 tlist; std::list::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& 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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list& 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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list& 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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list& 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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list& pValue) { std::list 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::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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list& pValue) { std::list 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::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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list& pValue) { std::list 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::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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list& pValue) { std::list 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::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& pValue) { std::list 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::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it); } } void X3DImporter::XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list& 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(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& 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& pPoint, std::list& pLine) { std::list::const_iterator pit = pPoint.begin(); std::list::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& pPolylineCoordIdx, std::list& pLineCoordIdx) { std::list::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::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& 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& pCoordIdx, std::vector& pFaces, unsigned int& pPrimitiveTypes) const { std::list f_data(pCoordIdx); std::vector 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::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::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& pColors, const bool pColorPerVertex) const { std::list tcol; // create RGBA array from RGB. for(std::list::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& pColors, const bool pColorPerVertex) const { std::list::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& pCoordIdx, const std::list& pColorIdx, const std::list& pColors, const bool pColorPerVertex) const { std::list tcol; // create RGBA array from RGB. for(std::list::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& pCoordIdx, const std::list& pColorIdx, const std::list& pColors, const bool pColorPerVertex) const { std::vector col_tgt_arr; std::list col_tgt_list; std::vector 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::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::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::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::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& pCoordIdx, const std::list& pNormalIdx, const std::list& pNormals, const bool pNormalPerVertex) const { std::vector tind; std::vector norm_arr_copy; // copy list to array because we are need indexed access to normals. norm_arr_copy.reserve(pNormals.size()); for(std::list::const_iterator it = pNormals.begin(); it != pNormals.end(); it++) norm_arr_copy.push_back(*it); if(pNormalPerVertex) { const std::list* 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::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::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& pNormals, const bool pNormalPerVertex) const { std::list::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& pCoordIdx, const std::list& pTexCoordIdx, const std::list& pTexCoords) const { std::vector texcoord_arr_copy; std::vector 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::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& pTexCoords) const { std::vector 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::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& pCoordIdx, const std::list& pVertices) const { aiMesh* tmesh; std::vector 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::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 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 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 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 as MetaString from 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 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& 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 , also in -> 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 mesh_list; std::list mat_list; std::list 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::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::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::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