diff --git a/code/AssetLib/X3D/X3DGeoHelper.cpp b/code/AssetLib/X3D/X3DGeoHelper.cpp index be41cc012..a9ac57e06 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.cpp +++ b/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -116,7 +116,7 @@ void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list &pPolylineCoo vert_set[6].Set(x1, y2, z1); \ vert_set[7].Set(x1, y1, z1) -void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices) { +void X3DGeoHelper::rect_parallel_epiped(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 diff --git a/code/AssetLib/X3D/X3DGeoHelper.h b/code/AssetLib/X3D/X3DGeoHelper.h index e730d3a9c..78e57f9da 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.h +++ b/code/AssetLib/X3D/X3DGeoHelper.h @@ -19,7 +19,7 @@ public: static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices); static void extend_point_to_line(const std::list &pPoint, std::list &pLine); static void polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx); - static void rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices); + static void rect_parallel_epiped(const aiVector3D &pSize, std::list &pVertices); static void coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index eeb1a53d7..bfe83a49f 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -46,11 +46,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" -#include -#include #include -#include // Header files, stdlib. #include @@ -72,59 +70,15 @@ const aiImporterDesc X3DImporter::Description = { "x3d x3db" }; -struct WordIterator { - using iterator_category = std::input_iterator_tag; - using value_type = const char *; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; +bool X3DImporter::isNodeEmpty(XmlNode &node) { + return node.first_child().empty(); +} - static const char *whitespace; - const char *mStart, *mEnd; +void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) { + if (isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty."); +} - WordIterator(const char *start, const char *end) : - mStart(start), - mEnd(end) { - mStart = start + ::strspn(start, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - } - WordIterator() : - mStart(0), - mEnd(0) {} - WordIterator(const WordIterator &other) : - mStart(other.mStart), - mEnd(other.mEnd) {} - WordIterator &operator=(const WordIterator &other) { - mStart = other.mStart; - mEnd = other.mEnd; - return *this; - } - - bool operator==(const WordIterator &other) const { return mStart == other.mStart; } - - bool operator!=(const WordIterator &other) const { return mStart != other.mStart; } - - WordIterator &operator++() { - mStart += strcspn(mStart, whitespace); - mStart += strspn(mStart, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - return *this; - } - WordIterator operator++(int) { - WordIterator result(*this); - ++(*this); - return result; - } - const char *operator*() const { return mStart; } -}; - -const char *WordIterator::whitespace = ", \t\r\n"; - -void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { +void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { static const size_t Uns_Skip_Len = 192; static const char *Uns_Skip[Uns_Skip_Len] = { // CAD geometry component @@ -203,16 +157,20 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { }; const std::string nn = node.name(); + + if (nn.empty()) { + const std::string nv = node.value(); + if (!nv.empty()) { + LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + "."); + return; + } + } + 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 (node.empty()) { - close_found = true; - break; - } } } @@ -223,7 +181,8 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { X3DImporter::X3DImporter() : mNodeElementCur(nullptr), - mScene(nullptr) { + mScene(nullptr), + mpIOHandler(nullptr) { // empty } @@ -265,9 +224,11 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { for (auto ¤tNode : node->children()) { const std::string ¤tName = currentNode.name(); if (currentName == "head") { - readMetadata(currentNode); + readHead(currentNode); } else if (currentName == "Scene") { readScene(currentNode); + } else { + skipUnsupportedNode("X3D", currentNode); } } } @@ -283,23 +244,25 @@ bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, b return false; } -void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + mpIOHandler = pIOHandler; + + Clear(); std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { throw DeadlyImportError("Could not open file for reading"); } std::string::size_type slashPos = pFile.find_last_of("\\/"); - pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); - ParseFile(pFile, pIOHandler); - pIOHandler->PopDirectory(); - - // mScene = pScene; pScene->mRootNode = new aiNode(pFile); pScene->mRootNode->mParent = nullptr; pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + //search for root node element mNodeElementCur = NodeElement_List.front(); @@ -342,7 +305,6 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS pScene->mLights[i] = *it++; } } - } const aiImporterDesc *X3DImporter::GetInfo() const { @@ -354,2540 +316,162 @@ struct meta_entry { std::string value; }; -void X3DImporter::readMetadata(XmlNode &node) { +void X3DImporter::readHead(XmlNode &node) { std::vector metaArray; for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "meta") { + checkNodeMustBeEmpty(node); meta_entry entry; if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { XmlParser::getStdStrAttribute(currentNode, "content", entry.value); metaArray.emplace_back(entry); } } + // TODO: check if other node types in head should be supported } mScene->mMetaData = aiMetadata::Alloc(static_cast(metaArray.size())); unsigned int i = 0; for (auto currentMeta : metaArray) { - mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value); + mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value)); ++i; } } -void X3DImporter::readScene(XmlNode &node) { +void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) { for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); - if (currentName == "Viewpoint") { - readViewpoint(currentNode); + if (currentName == "Shape") + readShape(currentNode); + else if (currentName == "Group") { + startReadGroup(currentNode); + readChildNodes(currentNode, "Group"); + endReadGroup(); + } else if (currentName == "StaticGroup") { + startReadStaticGroup(currentNode); + readChildNodes(currentNode, "StaticGroup"); + endReadStaticGroup(); + } else if (currentName == "Transform") { + startReadTransform(currentNode); + readChildNodes(currentNode, "Transform"); + endReadTransform(); + } else if (currentName == "Switch") { + startReadSwitch(currentNode); + readChildNodes(currentNode, "Switch"); + endReadSwitch(); + } else if (currentName == "DirectionalLight") { + readDirectionalLight(currentNode); + } else if (currentName == "PointLight") { + readPointLight(currentNode); + } else if (currentName == "SpotLight") { + readSpotLight(currentNode); + } else if (currentName == "Inline") { + readInline(currentNode); + } else if (!checkForMetadataNode(currentNode)) { + skipUnsupportedNode(pParentNodeName, currentNode); } } } -void X3DImporter::readViewpoint(XmlNode &node) { - for (auto currentNode : node.children()) { - //const std::string ¤tName = currentNode.name(); - } +void X3DImporter::readScene(XmlNode &node) { + ParseHelper_Group_Begin(true); + readChildNodes(node, "Scene"); + ParseHelper_Node_Exit(); } -void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaBoolean *boolean = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - boolean = new X3DNodeElementMetaBoolean(parent); - for (size_t i = 0; i < values.size(); ++i) { - bool current_boolean = false; - if (values[i] == "true") { - current_boolean = true; - } - boolean->Value.emplace_back(current_boolean); +/*********************************************************************************************************************************************/ +/************************************************************ Functions: find set ************************************************************/ +/*********************************************************************************************************************************************/ + +bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **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++) -void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaDouble *doubleNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - doubleNode = new X3DNodeElementMetaDouble(parent); - for (size_t i = 0; i < values.size(); ++i) { - double current_double = static_cast(fast_atof(values[i].c_str())); - doubleNode->Value.emplace_back(current_double); - } - } -} - -void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaFloat *floatNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - floatNode = new X3DNodeElementMetaFloat(parent); - for (size_t i = 0; i < values.size(); ++i) { - float current_float = static_cast(fast_atof(values[i].c_str())); - floatNode->Value.emplace_back(current_float); - } - } -} - -void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaInt *intNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - intNode = new X3DNodeElementMetaInt(parent); - for (size_t i = 0; i < values.size(); ++i) { - int current_int = static_cast(std::atoi(values[i].c_str())); - intNode->Value.emplace_back(current_int); - } - } -} - -void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent); - if (XmlParser::getStdStrAttribute(node, "name", val)) { - setNode->Name = val; - } - - if (XmlParser::getStdStrAttribute(node, "reference", val)) { - setNode->Reference = val; - } -} - -void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaString *strNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - strNode = new X3DNodeElementMetaString(parent); - for (size_t i = 0; i < values.size(); ++i) { - strNode->Value.emplace_back(values[i]); - } - } -} - -void X3DImporter::ParseDirectionalLight(XmlNode &node) { - std::string def, use; - float ambientIntensity = 0; - aiColor3D color(1, 1, 1); - aiVector3D direction(0, 0, -1); - bool global = false; - float intensity = 1; - bool on = true; - X3DNodeElementBase *ne = nullptr; - - //MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); - //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur); - if (!def.empty()) - ne->ID = def; - else - ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "DirectionalLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_PointLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - aiColor3D color(1, 1, 1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); - if (!def.empty()) ne->ID = def; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "PointLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_SpotLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - float beamWidth = 0.7854f; - aiColor3D color(1, 1, 1); - float cutOffAngle = 1.570796f; - aiVector3D direction(0, 0, -1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); - if (!def.empty()) - ne->ID = def; - - if (beamWidth > cutOffAngle) - beamWidth = cutOffAngle; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "SpotLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_Group() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_GroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or -// contain any USE references outside the StaticGroup. -void X3DImporter::ParseNode_Grouping_StaticGroup() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_StaticGroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child -// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing -// is chosen. -void X3DImporter::ParseNode_Grouping_Switch() { - std::string def, use; - int32_t whichChoice = -1; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) NodeElement_Cur->ID = def; - - // also set values specific to this type of group - ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true; - ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_SwitchEnd() { - // just exit from node. Defined choice will be accepted at postprocessing stage. - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. -// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate -// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the -// equivalent transformation matrices, -// P' = T * C * R * SR * S * -SR * -C * P -void X3DImporter::ParseNode_Grouping_Transform() { - aiVector3D center(0, 0, 0); - float rotation[4] = { 0, 0, 1, 0 }; - aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed - float scale_orientation[4] = { 0, 0, 1, 0 }; - aiVector3D translation(0, 0, 0); - aiMatrix4x4 matr, tmatr; - std::string use, def; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f); - if (an == "rotation") { - std::vector tvec; - - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); - - memcpy(rotation, tvec.data(), sizeof(rotation)); - - continue; - } - - if (an == "scaleOrientation") { - std::vector tvec; - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) { - throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); - } - - ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); - - continue; - } - - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) { - NodeElement_Cur->ID = def; - } - - // - // also set values specific to this type of group - // - // calculate transformation matrix - aiMatrix4x4::Translation(translation, matr); // T - aiMatrix4x4::Translation(center, tmatr); // C - matr *= tmatr; - aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R - matr *= tmatr; - aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR - matr *= tmatr; - aiMatrix4x4::Scaling(scale, tmatr); // S - matr *= tmatr; - aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR - matr *= tmatr; - aiMatrix4x4::Translation(-center, tmatr); // -C - matr *= tmatr; - // and assign it - ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) { - ParseHelper_Node_Exit(); - } - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_TransformEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -void X3DImporter::ParseNode_Geometry2D_Arc2D() { - std::string def, use; - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Arc2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping -// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius -// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater -// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has -// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between -// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. -// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then -// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point -// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when -// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_ArcClose2D() { - std::string def, use; - std::string closureType("PIE"); - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - bool solid = false; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // create point list of geometry object. - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg - // add chord or two radiuses only if not a circle was defined - if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - if ((closureType == "PIE") || (closureType == "\"PIE\"")) - vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line - else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) - Throw_IncorrectAttrValue("closureType"); - - vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size(); - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "ArcClose2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Circle2D() { - std::string def, use; - float radius = 1; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Circle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the -// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. -// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely -// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall -// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of -// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_Disk2D() { - std::string def, use; - float innerRadius = 0; - float outerRadius = 1; - bool solid = false; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne); - } else { - std::list tlist_o, tlist_i; - - if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius"); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object. - ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle - if (innerRadius == 0.0f) { // make filled disk - // in tlist_o we already have points of circle. just copy it and sign as polygon. - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size(); - } else if (innerRadius == outerRadius) { // make circle - // in tlist_o we already have points of circle. convert it to line set. - GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - } else { // make disk - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle - // - // create quad list from two point lists - // - if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. - - // add all quads except last - for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { - // do not forget - CCW direction - vlist.push_back(*it_i++); // 1st point - vlist.push_back(*it_o++); // 2nd point - vlist.push_back(*it_o); // 3rd point - vlist.push_back(*it_i); // 4th point - } - - // add last quad - vlist.push_back(*tlist_i.end()); // 1st point - vlist.push_back(*tlist_o.end()); // 2nd point - vlist.push_back(*tlist_o.begin()); // 3rd point - vlist.push_back(*tlist_o.begin()); // 4th point - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Disk2D"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polyline2D() { - std::string def, use; - std::list lineSegments; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // - // convert read point list of geometry object to line set. - // - std::list tlist; - - // convert vec2 to vec3 - for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) - tlist.push_back(aiVector3D(it2->x, it2->y, 0)); - - // convert point set to line set - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polyline2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polypoint2D() { - std::string def, use; - std::list point; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polypoint2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Rectangle2D() { - std::string def, use; - aiVector2D size(2, 2); - bool solid = false; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - float x1 = -size.x / 2.0f; - float x2 = size.x / 2.0f; - float y1 = -size.y / 2.0f; - float y2 = size.y / 2.0f; - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point - vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point - vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point - vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Rectangle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() { - std::string def, use; - bool solid = false; - std::list vertices; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne); - } else { - if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "TriangleSet2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. -// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes -// respectively and each component value shall be greater than zero. -void X3DImporter::ParseNode_Geometry3D_Box() { - std::string def, use; - bool solid = true; - aiVector3D size(2, 2, 2); - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Box"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cone() { - std::string use, def; - bool bottom = true; - float bottomRadius = 1; - float height = 2; - bool side = true; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tvec; // temp array for vertices. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cone or parts according to flags. - if (side) { - StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); - } else if (bottom) { - StandardShapes::MakeCircle(bottomRadius, tess, tvec); - height = -(height / 2); - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - it->y = height; // y - because circle made in oXZ. - } - - // copy data from temp array - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it); - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cone"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cylinder() { - std::string use, def; - bool bottom = true; - float height = 2; - float radius = 1; - bool side = true; - bool solid = true; - bool top = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tside; // temp array for vertices of side. - std::vector tcir; // temp array for vertices of circle. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cilynder or parts according to flags. - if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); - - height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. - if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); - // copy data from temp arrays - std::list &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias. - - for (std::vector::iterator it = tside.begin(); it != tside.end(); ++it) - vlist.push_back(*it); - - if (top) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - if (bottom) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = -height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cylinder"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single -// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described -// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate -// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. -// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. -void X3DImporter::ParseNode_Geometry3D_ElevationGrid() { - std::string use, def; - bool ccw = true; - bool colorPerVertex = true; - float creaseAngle = 0; - std::vector height; - bool normalPerVertex = true; - bool solid = true; - int32_t xDimension = 0; - float xSpacing = 1; - int32_t zDimension = 0; - float zSpacing = 1; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne); - } else { - if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); - if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); - if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\""); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience - - { // create grid vertices list - std::vector::const_iterator he_it = height.begin(); - - for (int32_t zi = 0; zi < zDimension; zi++) // rows - { - for (int32_t xi = 0; xi < xDimension; xi++) // columns - { - aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); - - grid_alias.Vertices.push_back(tvec); - ++he_it; - } - } - } // END: create grid vertices list - // - // create faces list. In "coordIdx" format - // - // check if we have quads - if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. - for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { - grid_alias.CoordIdx.push_back(static_cast(i)); - grid_alias.CoordIdx.push_back(static_cast(i + 1)); - grid_alias.CoordIdx.push_back(-1); - } - } else // two or more elements in every dimension is set. create quad set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4; - for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows - { - for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns - { - // points direction in face. - if (ccw) { - // CCW: - // 3 2 - // 0 1 - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - } else { - // CW: - // 0 1 - // 3 2 - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - } // if(ccw) else - - grid_alias.CoordIdx.push_back(-1); - } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) - } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) - } // if((xDimension < 2) || (zDimension < 2)) else - - grid_alias.ColorPerVertex = colorPerVertex; - grid_alias.NormalPerVertex = normalPerVertex; - grid_alias.CreaseAngle = creaseAngle; - grid_alias.Solid = solid; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("ElevationGrid"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid"); - - MACRO_NODECHECK_LOOPEND("ElevationGrid"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } // if(!mReader->isEmptyElement()) else - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -template -static void GeometryHelper_Extrusion_CurveIsClosed(std::vector &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { - size_t cur_sz = pCurve.size(); - - pCurveIsClosed = false; - // for curve with less than four points checking is have no sense, - if (cur_sz < 4) return; - - for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { - // search for first point of duplicated part. - if (pCurve[0] == pCurve[s]) { - bool found = true; - - // check if tail(indexed by b2) is duplicate of head(indexed by b1). - for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { - if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. - found = false; - - break; - } - } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) - - // if duplicate tail is found then drop or not it depending on flags. - if (found) { - pCurveIsClosed = true; - if (pDropTail) { - if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. - - pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail - } - - break; - } // if(found) - } // if(pCurve[0] == pCurve[s]) - } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) -} - -static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed) { - const size_t spine_idx_last = pSpine.size() - 1; - aiVector3D tvec; - - if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases - { - if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. - // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) - // in tail are removed. - // So, last point in pSpine is a spine[n - 2] - tvec = pSpine[1] - pSpine[spine_idx_last]; - } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] - tvec = pSpine[1] - pSpine[0]; - } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is - // the spine[0]. - tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; - } - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) - else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). - tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else - - return tvec.Normalize(); -} - -static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed, - const aiVector3D pVecZ_Prev) { - const aiVector3D zero_vec(0); - const size_t spine_idx_last = pSpine.size() - 1; - - aiVector3D tvec; - - // at first special cases - if (pSpine.size() < 3) // spine have not enough points for vector calculations. - { - tvec.Set(0, 0, 1); - } else if (pSpine_PointIdx == 0) // special case: first point - { - if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. - { - tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); - } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. - { - bool found = false; - - // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) - // then the Z-axis for the first spine point with a defined Z-axis is used." - // Walk through spine and find Z. - for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { - // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) - tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); - found = !tvec.Equal(zero_vec); - } - - // if entire spine are collinear then use OZ axis. - if (!found) tvec.Set(0, 0, 1); - } // if(pSpine_Closed) else - } // else if(pSpine_PointIdx == 0) - else if (pSpine_PointIdx == spine_idx_last) // special case: last point - { - if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. - tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } else { // vector Z for last point of not closed curve is previous vector Z. - tvec = pVecZ_Prev; - } - } else // regular point - { - tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } - - // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis - // is flipped (multiplied by -1). - if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; - - return tvec.Normalize(); -} - -// -void X3DImporter::ParseNode_Geometry3D_Extrusion() { - std::string use, def; - bool beginCap = true; - bool ccw = true; - bool convex = true; - float creaseAngle = 0; - std::vector crossSection; - bool endCap = true; - std::vector orientation; - std::vector scale; - bool solid = true; - std::vector spine; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne); - } else { - // - // check if default values must be assigned - // - if (spine.size() == 0) { - spine.resize(2); - spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); - } else if (spine.size() == 1) { - throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); - } - - if (crossSection.size() == 0) { - crossSection.resize(5); - crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); - } - - { // orientation - size_t ori_size = orientation.size() / 4; - - if (ori_size < spine.size()) { - float add_ori[4]; // values that will be added - - if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. - { - add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; - } else // else - use default values - { - add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; - } - - orientation.reserve(spine.size() * 4); - for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) - orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); - } - - if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); - } // END: orientation - - { // scale - if (scale.size() < spine.size()) { - aiVector2D add_sc; - - if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. - add_sc = scale[0]; - else // else - use default values - add_sc.Set(1, 1); - - scale.reserve(spine.size()); - for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) - scale.push_back(add_sc); - } - } // END: scale - // - // create and if needed - define new geometry object. - // - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience - // assign part of input data - ext_alias.CCW = ccw; - ext_alias.Convex = convex; - ext_alias.CreaseAngle = creaseAngle; - ext_alias.Solid = solid; - - // - // How we done it at all? - // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector - // are applied vor every basis. - // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position - // using relative spine point. - // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if - // needed. While createing CootdIdx is taking in account CCW flag. - // 4. The last step: create Vertices list. - // - bool spine_closed; // flag: true if spine curve is closed. - bool cross_closed; // flag: true if cross curve is closed. - std::vector basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. - std::vector> pointset_arr; // array of point sets: cross curves. - - // detect closed curves - GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. - GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. - // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. - if (spine_closed) { - beginCap |= endCap; - endCap = false; - } - - { // 1. Calculate array of basises. - aiMatrix4x4 rotmat; - aiVector3D vecX(0), vecY(0), vecZ(0); - - basis_arr.resize(spine.size()); - for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { - aiVector3D tvec; - - // get axises of basis. - vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); - vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); - vecX = (vecY ^ vecZ).Normalize(); - // get rotation matrix and apply "orientation" to basis - aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); - tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; - tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; - tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; - } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) - } // END: 1. Calculate array of basises - - { // 2. Create array of point sets. - aiMatrix4x4 scmat; - std::vector tcross(crossSection.size()); - - pointset_arr.resize(spine.size()); - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - aiVector3D tc23vec; - - tc23vec.Set(scale[spi].x, 0, scale[spi].y); - aiMatrix4x4::Scaling(tc23vec, scmat); - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - aiVector3D tvecX, tvecY, tvecZ; - - tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); - // apply scaling to point - tcross[cri] = scmat * tc23vec; - // - // transfer point to new basis - // calculate coordinate in new basis - tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; - tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; - tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; - // apply new coordinates and translate it to spine point. - tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; - } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) - - pointset_arr[spi] = tcross; // store transferred point set - } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) - } // END: 2. Create array of point sets. - - { // 3. Create CoordIdx. - // add caps if needed - if (beginCap) { - // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. - for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - if (endCap) { - // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. - size_t beg = (pointset_arr.size() - 1) * crossSection.size(); - - for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - // add quads - for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { - const size_t cr_sz = crossSection.size(); - const size_t cr_last = crossSection.size() - 1; - - size_t right_col; // hold index basis for points of quad placed in right column; - - if (spi != spi_e) - right_col = spi + 1; - else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. - right_col = 0; - else - break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. - - for (size_t cri = 0; cri < cr_sz; cri++) { - if (cri != cr_last) { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + cri + 1), - static_cast(spi * cr_sz + cri + 1)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. - { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + 0), - static_cast(spi * cr_sz + 0)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } - } // for(size_t cri = 0; cri < cr_sz; cri++) - } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) - } // END: 3. Create CoordIdx. - - { // 4. Create vertices list. - // just copy all vertices - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - ext_alias.Vertices.push_back(pointset_arr[spi][cri]); - } - } - } // END: 4. Create vertices list. - //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); - //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Extrusion"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, -// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, -// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() { - std::string use, def; - bool ccw = true; - std::vector colorIndex; - bool colorPerVertex = true; - bool convex = true; - std::vector coordIndex; - float creaseAngle = 0; - std::vector normalIndex; - bool normalPerVertex = true; - bool solid = true; - std::vector texCoordIndex; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne); - } else { - // check data - if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); - - ne_alias.CCW = ccw; - ne_alias.ColorIndex = colorIndex; - ne_alias.ColorPerVertex = colorPerVertex; - ne_alias.Convex = convex; - ne_alias.CoordIndex = coordIndex; - ne_alias.CreaseAngle = creaseAngle; - ne_alias.NormalIndex = normalIndex; - ne_alias.NormalPerVertex = normalPerVertex; - ne_alias.Solid = solid; - ne_alias.TexCoordIndex = texCoordIndex; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Coordinate")) { - ParseNode_Rendering_Coordinate(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet"); - - MACRO_NODECHECK_LOOPEND("IndexedFaceSet"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Sphere() { - std::string use, def; - ai_real radius = 1; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne); - } else { - const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property - - std::vector tlist; - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - StandardShapes::MakeSphere(tess, tlist); - // copy data from temp array and apply scale - for (std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) { - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius); - } - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Sphere"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - - - -void X3DImporter::readMetadataObject(XmlNode &node) { - const std::string &name = node.name(); - if (name == "MetadataBoolean") { - readMetadataBoolean(node, mNodeElementCur); - } else if (name == "MetadataDouble") { - readMetadataDouble(node, mNodeElementCur); - } else if (name == "MetadataFloat") { - readMetadataFloat(node, mNodeElementCur); - } else if (name == "MetadataInteger") { - readMetadataInteger(node, mNodeElementCur); - } else if (name == "MetadataSet") { - readMetadataSet(node, mNodeElementCur); - } else if (name == "MetadataString") { - readMetadataString(node, mNodeElementCur); - } -} - - -aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() { - X3DNodeElementBase *cur_node = nullptr; - std::list matr; - aiMatrix4x4 out_matr; - - // starting walk from current element to root - cur_node = cur_node; - if (cur_node != nullptr) { - do { - // if cur_node is group then store group transformation matrix in list. - if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation); - - cur_node = cur_node->Parent; - } while (cur_node != nullptr); - } - - // multiplicate all matrices in reverse order - for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) - out_matr = out_matr * (*rit); - - return out_matr; -} - -void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list &pList) const { - // walk through childs and find for metadata. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || - ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || - ((*el_it)->Type == X3DElemType::ENET_MetaString)) { - pList.push_back(*el_it); - } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { - PostprocessHelper_CollectMetadata(**el_it, pList); - } - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || - (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || - (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { - return true; - } return false; } -bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || - (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || - (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || - (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || - (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || - (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || - (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || - (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || - (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || - (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || - (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || - (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { - return true; +bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **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->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) { + found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement); + if (found) { + break; + } + } // for(std::list::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++) + +fne_fn_end: + + return found; +} + +bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + X3DNodeElementBase *tnd = mNodeElementCur; // 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 through parent nodes and check flag. + while (tnd != nullptr) { + if (tnd->Type == X3DElemType::ENET_Group) { + if (((X3DNodeElementGroup *)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 false; + return FindNodeElement_FromRoot(pID, pType, pElement); } } -void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list &pSceneLightList) const { - const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement); - aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); - aiLight *new_light = new aiLight; +/*********************************************************************************************************************************************/ +/************************************************************ Functions: parse set ***********************************************************/ +/*********************************************************************************************************************************************/ - new_light->mName = ne.ID; - new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; - new_light->mColorDiffuse = ne.Color * ne.Intensity; - new_light->mColorSpecular = ne.Color * ne.Intensity; - switch (pNodeElement.Type) { - case CX3DImporter_NodeElement::ENET_DirectionalLight: - new_light->mType = aiLightSource_DIRECTIONAL; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; +void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) { + X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent. - break; - case CX3DImporter_NodeElement::ENET_PointLight: - new_light->mType = aiLightSource_POINT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - - break; - case CX3DImporter_NodeElement::ENET_SpotLight: - new_light->mType = aiLightSource_SPOT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - new_light->mAngleInnerCone = ne.BeamWidth; - new_light->mAngleOuterCone = ne.CutOffAngle; - - break; - default: - throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + "."); + // if we are adding not the root element then add new element to current element child list. + if (mNodeElementCur != nullptr) { + mNodeElementCur->Children.push_back(new_group); } - pSceneLightList.push_back(new_light); + NodeElement_List.push_back(new_group); // it's a new element - add it to list. + mNodeElementCur = new_group; // switch current element to new one. } -void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const { - // check argument - if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); - if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); +void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) { + mNodeElementCur->Children.push_back(pNode); // add new element to current element child list. + mNodeElementCur = pNode; // switch current element to new one. +} - *pMaterial = new aiMaterial; - aiMaterial &taimat = **pMaterial; // creating alias for convenience. - - // at this point pNodeElement point to node. Walk through childs and add all stored data. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) { - aiColor3D tcol3; - float tvalf; - CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it); - - tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; - taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); - taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); - taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); - taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); - tvalf = 1; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); - taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); - tvalf = 1.0f - tnemat.Transparency; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); - } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) { - CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it); - aiString url_str(tnetex.URL.c_str()); - int mode = aiTextureOp_Multiply; - - taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); - taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) { - aiUVTransform trans; - CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it); - - trans.mTranslation = tnetextr.Translation - tnetextr.Center; - trans.mScaling = tnetextr.Scale; - trans.mRotation = tnetextr.Rotation; - taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const { - // check argument - if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); - if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); - - /************************************************************************************************************************************/ - /************************************************************ Geometry2D ************************************************************/ - /************************************************************************************************************************************/ - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) { - CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. +void X3DImporter::ParseHelper_Node_Exit() { + // check if we can walk up. + if (mNodeElementCur != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; } - /************************************************************************************************************************************/ - /************************************************************ Geometry3D ************************************************************/ - /************************************************************************************************************************************/ - // - // Predefined figures - // - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) { - CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. - } - // - // Parametric figures - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) { - CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience - - // at first create mesh from existing vertices. - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices); - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) - // - // Indexed primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) - - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ - IndexedTriangleStripSet: " + - to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices); - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) - - // - // Primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 1); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if (nullptr == *pMesh) { - break; - } - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 3); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) - - throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + "."); -} - -void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const { - X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin(); - X3DElementList::const_iterator chit_end = pNodeElement.Children.end(); - std::list SceneNode_Child; - std::list SceneNode_Mesh; - - // At first read all metadata - Postprocess_CollectMetadata(pNodeElement, pSceneNode); - // check if we have deal with grouping node. Which can contain transformation or switch - if (pNodeElement.Type == X3DElemType::ENET_Group) { - const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience - - pSceneNode.mTransformation = tne_group.Transformation; - if (tne_group.UseChoice) { - // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. - if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { - chit_begin = pNodeElement.Children.end(); - chit_end = pNodeElement.Children.end(); - } else { - for (size_t i = 0; i < (size_t)tne_group.Choice; i++) - ++chit_begin; // forward iterator to chosen node. - - chit_end = chit_begin; - ++chit_end; // point end iterator to next element after chosen node. - } - } // if(tne_group.UseChoice) - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group) - - // Reserve memory for fast access and check children. - for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. - if ((*it)->Type == X3DElemType::ENET_Group) { - // if child is group then create new node and do recursive call. - aiNode *new_node = new aiNode; - - new_node->mName = (*it)->ID; - new_node->mParent = &pSceneNode; - SceneNode_Child.push_back(new_node); - Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); - } else if ((*it)->Type == X3DElemType::ENET_Shape) { - // shape can contain only one geometry and one appearance nodes. - Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); - } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || - ((*it)->Type == X3DElemType::ENET_SpotLight)) { - Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList); - } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata - { - throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + "."); - } - } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) - - // copy data about children and meshes to aiNode. - if (!SceneNode_Child.empty()) { - std::list::const_iterator it = SceneNode_Child.begin(); - - pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); - pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; - for (size_t i = 0; i < pSceneNode.mNumChildren; i++) - pSceneNode.mChildren[i] = *it++; - } - - if (!SceneNode_Mesh.empty()) { - std::list::const_iterator it = SceneNode_Mesh.begin(); - - pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); - pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; - for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) - pSceneNode.mMeshes[i] = *it++; - } - - // that's all. return to previous deals -} - -void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list &pNodeMeshInd, - std::list &pSceneMeshList, std::list &pSceneMaterialList) const { - aiMaterial *tmat = nullptr; - aiMesh *tmesh = nullptr; - X3DElemType mesh_type = X3DElemType::ENET_Invalid; - unsigned int mat_ind = 0; - - for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { - if (PostprocessHelper_ElementIsMesh((*it)->Type)) { - Postprocess_BuildMesh(**it, &tmesh); - if (tmesh != nullptr) { - // if mesh successfully built then add data about it to arrays - pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); - pSceneMeshList.push_back(tmesh); - // keep mesh type. Need above for texture coordinate generation. - mesh_type = (*it)->Type; - } - } else if ((*it)->Type == X3DElemType::ENET_Appearance) { - Postprocess_BuildMaterial(**it, &tmat); - if (tmat != nullptr) { - // if material successfully built then add data about it to array - mat_ind = static_cast(pSceneMaterialList.size()); - pSceneMaterialList.push_back(tmat); - } - } - } // for(std::list::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++) - - // associate read material with read mesh. - if ((tmesh != nullptr) && (tmat != nullptr)) { - tmesh->mMaterialIndex = mat_ind; - // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. - if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { - int32_t tm; - aiVector3D tvec3; - - switch (mesh_type) { - case X3DElemType::ENET_Box: - tm = aiTextureMapping_BOX; - break; - case X3DElemType::ENET_Cone: - case X3DElemType::ENET_Cylinder: - tm = aiTextureMapping_CYLINDER; - break; - case X3DElemType::ENET_Sphere: - tm = aiTextureMapping_SPHERE; - break; - default: - tm = aiTextureMapping_PLANE; - break; - } // switch(mesh_type) - - tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); - } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) - } // if((tmesh != nullptr) && (tmat != nullptr)) -} - -void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const { - X3DElementList meta_list; - size_t meta_idx; - - PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. - if (!meta_list.empty()) { - if (pSceneNode.mMetaData != nullptr) { - throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); - } - - // copy collected metadata to output node. - pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); - meta_idx = 0; - for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { - CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it; - - // due to limitations we can add only first element of value list. - // Add an element according to its type. - if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) { - if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) { - if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) { - if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) { - if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) { - if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) { - aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data()); - - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); - } - } else { - throw DeadlyImportError("Postprocess. Unknown metadata type."); - } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else - } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) - } // if( !meta_list.empty() ) } #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 559e4932f..c9509a035 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H +#include "X3DImporter_Node.hpp" + #include #include #include @@ -51,7 +53,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include namespace Assimp { @@ -68,6 +69,21 @@ inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::s "\" from string to array of floats."); } +inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of doubles."); +} + +inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of booleans."); +} + +inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of integers."); +} + inline void Throw_DEF_And_USE(const std::string &nodeName) { throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); } @@ -225,410 +241,8 @@ inline void LogInfo(const std::string &message) { /// /// That's all for now. Enjoy /// -enum class X3DElemType { - ENET_Group, ///< Element has type "Group". - ENET_MetaBoolean, ///< Element has type "Metadata boolean". - ENET_MetaDouble, ///< Element has type "Metadata double". - ENET_MetaFloat, ///< Element has type "Metadata float". - ENET_MetaInteger, ///< Element has type "Metadata integer". - ENET_MetaSet, ///< Element has type "Metadata set". - ENET_MetaString, ///< Element has type "Metadata string". - ENET_Arc2D, ///< Element has type "Arc2D". - ENET_ArcClose2D, ///< Element has type "ArcClose2D". - ENET_Circle2D, ///< Element has type "Circle2D". - ENET_Disk2D, ///< Element has type "Disk2D". - ENET_Polyline2D, ///< Element has type "Polyline2D". - ENET_Polypoint2D, ///< Element has type "Polypoint2D". - ENET_Rectangle2D, ///< Element has type "Rectangle2D". - ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". - ENET_Box, ///< Element has type "Box". - ENET_Cone, ///< Element has type "Cone". - ENET_Cylinder, ///< Element has type "Cylinder". - ENET_Sphere, ///< Element has type "Sphere". - ENET_ElevationGrid, ///< Element has type "ElevationGrid". - ENET_Extrusion, ///< Element has type "Extrusion". - ENET_Coordinate, ///< Element has type "Coordinate". - ENET_Normal, ///< Element has type "Normal". - ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". - ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". - ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". - ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". - ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". - ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". - ENET_LineSet, ///< Element has type "LineSet". - ENET_PointSet, ///< Element has type "PointSet". - ENET_TriangleSet, ///< Element has type "TriangleSet". - ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". - ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". - ENET_Color, ///< Element has type "Color". - ENET_ColorRGBA, ///< Element has type "ColorRGBA". - ENET_Shape, ///< Element has type "Shape". - ENET_Appearance, ///< Element has type "Appearance". - ENET_Material, ///< Element has type "Material". - ENET_ImageTexture, ///< Element has type "ImageTexture". - ENET_TextureTransform, ///< Element has type "TextureTransform". - ENET_DirectionalLight, ///< Element has type "DirectionalLight". - ENET_PointLight, ///< Element has type "PointLight". - ENET_SpotLight, ///< Element has type "SpotLight". - ENET_Invalid ///< Element has invalid type and possible contain invalid data. -}; - -struct X3DNodeElementBase { - X3DNodeElementBase *Parent; - std::string ID; - std::list Children; - X3DElemType Type; - -protected: - X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : - Type(type), Parent(pParent) { - // empty - } -}; - -/// This struct hold value. -struct CX3DImporter_NodeElement_Color : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} - -}; // struct CX3DImporter_NodeElement_Color - -/// This struct hold value. -struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} - -}; // struct CX3DImporter_NodeElement_ColorRGBA - -/// This struct hold value. -struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_Coordinate - -/// This struct hold value. -struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} - -}; // struct CX3DImporter_NodeElement_Normal - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureCoordinate - -/// Two-dimensional figure. -struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Solid(true) {} - -}; // class CX3DImporter_NodeElement_Geometry2D - -/// Three-dimensional body. -struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { - // empty - } -}; // class CX3DImporter_NodeElement_Geometry3D - -/// Uniform rectangular grid of varying height. -struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D { - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with indexed vertices. -struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, - /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results - /// even if the convex field is FALSE. - bool Convex; - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with set of vertices. -struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(type, pParent) {} - -}; // class CX3DImporter_NodeElement_Set - -/// This struct hold value. -struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} -}; // struct CX3DImporter_NodeElement_Shape - -/// This struct hold value. -struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} - -}; // struct CX3DImporter_NodeElement_Appearance - -struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase { - float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. - aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. - aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. - float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. - aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. - float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Material, pParent), - AmbientIntensity(0.0f), - DiffuseColor(), - EmissiveColor(), - Shininess(0.0f), - SpecularColor(), - Transparency(1.0f) { - // empty - } -}; // class CX3DImporter_NodeElement_Material - -/// This struct hold value. -struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase { - /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated - /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are - /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. - bool RepeatS; - bool RepeatT; ///< See \ref RepeatS. - std::string URL; ///< URL of the texture. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} - -}; // struct CX3DImporter_NodeElement_ImageTexture - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase { - aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. - float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. - aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. - aiVector2D Translation; ///< Specifies a translation of the texture coordinates. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureTransform - -struct CX3DNodeElementGroup : X3DNodeElementBase { - aiMatrix4x4 Transformation; ///< Transformation matrix. - - /// As you know node elements can use already defined node elements when attribute "USE" is defined. - /// Standard search when looking for an element in the whole scene graph, existing at this moment. - /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. - bool Static; - - bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. - int32_t Choice; ///< Number of the child which will be kept. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pStatic - static node flag. - CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : - X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} -}; - -struct X3DNodeElementMeta : X3DNodeElementBase { - std::string Name; ///< Name of metadata object. - std::string Reference; - - virtual ~X3DNodeElementMeta() { - // empty - } - -protected: - X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : - X3DNodeElementBase(type, parent) { - // empty - } -}; - -struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaDouble : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaInt : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaSet : public X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaString : X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { - // empty - } -}; - -/// \struct CX3DImporter_NodeElement_Light -/// This struct hold value. -struct X3DNodeNodeElementLight : X3DNodeElementBase { - float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. - aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. - aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. - /// \var Global - /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. - /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. - bool Global; - float Intensity; ///< Specifies the brightness of the direct emission from the light. - /// \var Attenuation - /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor - /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. - aiVector3D Attenuation; - aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. - float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. - float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. - float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). - - /// Constructor - /// \param [in] pParent - pointer to parent node. - /// \param [in] pLightType - type of the light source. - X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pLightType, pParent) {} - -}; // struct CX3DImporter_NodeElement_Light - -using X3DElementList = std::list; +using X3DElementList = std::list; class X3DImporter : public BaseImporter { public: @@ -654,18 +268,113 @@ public: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); - void readMetadata(XmlNode &node); - void readScene(XmlNode &node); - void readViewpoint(XmlNode &node); - void readMetadataObject(XmlNode &node); - void ParseDirectionalLight(XmlNode &node); - void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const; private: + bool isNodeEmpty(XmlNode &node); + void checkNodeMustBeEmpty(XmlNode &node); + void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node); + void readHead(XmlNode &node); + void readChildNodes(XmlNode &node, const std::string &pParentNodeName); + void readScene(XmlNode &node); + + bool FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + void ParseHelper_Group_Begin(const bool pStatic = false); + void ParseHelper_Node_Enter(X3DNodeElementBase *pNode); + void ParseHelper_Node_Exit(); + + // 2D geometry + void readArc2D(XmlNode &node); + void readArcClose2D(XmlNode &node); + void readCircle2D(XmlNode &node); + void readDisk2D(XmlNode &node); + void readPolyline2D(XmlNode &node); + void readPolypoint2D(XmlNode &node); + void readRectangle2D(XmlNode &node); + void readTriangleSet2D(XmlNode &node); + + // 3D geometry + void readBox(XmlNode &node); + void readCone(XmlNode &node); + void readCylinder(XmlNode &node); + void readElevationGrid(XmlNode &node); + void readExtrusion(XmlNode &node); + void readIndexedFaceSet(XmlNode &node); + void readSphere(XmlNode &node); + + // group + void startReadGroup(XmlNode &node); + void endReadGroup(); + void startReadStaticGroup(XmlNode &node); + void endReadStaticGroup(); + void startReadSwitch(XmlNode &node); + void endReadSwitch(); + void startReadTransform(XmlNode &node); + void endReadTransform(); + + // light + void readDirectionalLight(XmlNode &node); + void readPointLight(XmlNode &node); + void readSpotLight(XmlNode &node); + + // metadata + bool checkForMetadataNode(XmlNode &node); + void childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName); + void readMetadataBoolean(XmlNode &node); + void readMetadataDouble(XmlNode &node); + void readMetadataFloat(XmlNode &node); + void readMetadataInteger(XmlNode &node); + void readMetadataSet(XmlNode &node); + void readMetadataString(XmlNode &node); + + // networking + void readInline(XmlNode &node); + + // postprocessing + aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const; + void PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const; + bool PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const; + bool PostprocessHelper_ElementIsMesh(const X3DElemType pType) const; + void Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const; + void Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const; + void Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const; + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const; + void Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const; + void Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const; + + // rendering + void readColor(XmlNode &node); + void readColorRGBA(XmlNode &node); + void readCoordinate(XmlNode &node); + void readIndexedLineSet(XmlNode &node); + void readIndexedTriangleFanSet(XmlNode &node); + void readIndexedTriangleSet(XmlNode &node); + void readIndexedTriangleStripSet(XmlNode &node); + void readLineSet(XmlNode &node); + void readPointSet(XmlNode &node); + void readTriangleFanSet(XmlNode &node); + void readTriangleSet(XmlNode &node); + void readTriangleStripSet(XmlNode &node); + void readNormal(XmlNode &node); + + // shape + void readShape(XmlNode &node); + void readAppearance(XmlNode &node); + void readMaterial(XmlNode &node); + + // texturing + void readImageTexture(XmlNode &node); + void readTextureCoordinate(XmlNode &node); + void readTextureTransform(XmlNode &node); + static const aiImporterDesc Description; X3DNodeElementBase *mNodeElementCur; aiScene *mScene; + IOSystem *mpIOHandler; }; // class X3DImporter } // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp new file mode 100644 index 000000000..171075556 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp @@ -0,0 +1,467 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry2D.cpp +/// \brief Parsing data from nodes of "Geometry2D" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +namespace Assimp { + +// +// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle +// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different +// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified. +void X3DImporter::readArc2D(XmlNode &node) { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Arc2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Arc2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readArcClose2D(XmlNode &node) { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "closureType", closureType); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_ArcClose2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // create point list of geometry object. + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, ((X3DNodeElementGeometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("ArcClose2D", "closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = ((X3DNodeElementGeometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ArcClose2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCircle2D(XmlNode &node) { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Circle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Circle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readDisk2D(XmlNode &node) { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "innerRadius", innerRadius); + XmlParser::getFloatAttribute(node, "outerRadius", outerRadius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Disk2D, ne); + } else { + std::list tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("Disk2D", "innerRadius"); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Disk2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::make_arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((X3DNodeElementGeometry2D *)ne)->Vertices = tlist_o; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + X3DGeoHelper::extend_point_to_line(tlist_o, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + X3DGeoHelper::make_arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Disk2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolyline2D(XmlNode &node) { + std::string def, use; + std::list lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "lineSegments", lineSegments); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polyline2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list tlist; + + // convert vec2 to vec3 + for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polyline2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolypoint2D(XmlNode &node) { + std::string def, use; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polypoint2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polypoint2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readRectangle2D(XmlNode &node) { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Rectangle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Rectangle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTriangleSet2D(XmlNode &node) { + std::string def, use; + bool solid = false; + std::list vertices; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "vertices", vertices); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_TriangleSet2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TriangleSet2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp new file mode 100644 index 000000000..4250bfb00 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp @@ -0,0 +1,981 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry3D.cpp +/// \brief Parsing data from nodes of "Geometry3D" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +// Header files, Assimp. +#include + +namespace Assimp +{ + +// +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::readBox(XmlNode &node) { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Box, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Box, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DGeoHelper::rect_parallel_epiped(size, ((X3DNodeElementGeometry3D *)ne)->Vertices); // get quad list + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Box"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCone(XmlNode &node) { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getFloatAttribute(node, "height", height); + XmlParser::getFloatAttribute(node, "bottomRadius", bottomRadius); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cone, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tvec;// temp array for vertices. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cone, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if(side) + { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } + else if(bottom) + { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for(std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((X3DNodeElementGeometry3D*)ne)->Vertices.push_back(*it); + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cone"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCylinder(XmlNode &node) { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getBoolAttribute(node, "top", top); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getFloatAttribute(node, "height", height); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cylinder, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tside;// temp array for vertices of side. + std::vector tcir;// temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cylinder, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list &vlist = ((X3DNodeElementGeometry3D *)ne)->Vertices; // just short alias. + + for(std::vector::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it); + + if(top) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + if(bottom) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = -height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cylinder"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::readElevationGrid(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getFloatArrayAttribute(node, "height", height); + XmlParser::getIntAttribute(node, "xDimension", xDimension); + XmlParser::getFloatAttribute(node, "xSpacing", xSpacing); + XmlParser::getIntAttribute(node, "zDimension", zDimension); + XmlParser::getFloatAttribute(node, "zSpacing", zSpacing); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ElevationGrid, ne); + } + else + { + if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); + if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) DeadlyImportError("Heights count must be equal to \"xDimension * zDimension\" in "); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementElevationGrid(X3DElemType::ENET_ElevationGrid, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementElevationGrid &grid_alias = *((X3DNodeElementElevationGrid *)ne); // create alias for conveience + + {// create grid vertices list + std::vector::const_iterator he_it = height.begin(); + + for(int32_t zi = 0; zi < zDimension; zi++)// rows + { + for(int32_t xi = 0; xi < xDimension; xi++)// columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + }// END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) + { + grid_alias.CoordIdx.push_back(static_cast(i)); + grid_alias.CoordIdx.push_back(static_cast(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } + else// two or more elements in every dimension is set. create quad set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 4; + for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows + { + for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns + { + // points direction in face. + if(ccw) + { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } + else + { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + }// if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + }// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + }// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + }// if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("ElevationGrid", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!mReader->isEmptyElement()) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + }// if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +template +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed) +{ + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if(cur_sz < 4) return; + + for(size_t s = 3, s_e = cur_sz; s < s_e; s++) + { + // search for first point of duplicated part. + if(pCurve[0] == pCurve[s]) + { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + { + if(pCurve[b1] != pCurve[b2]) + {// points not match: clear flag and break loop. + found = false; + + break; + } + }// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if(found) + { + pCurveIsClosed = true; + if(pDropTail) + { + if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail + } + + break; + }// if(found) + }// if(pCurve[0] == pCurve[s]) + }// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed) +{ + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases + { + if(pSpine_Closed) + {// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } + else if(pSpine_PointIdx == 0) + {// The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } + else + {// The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else + {// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) +{ + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if(pSpine.size() < 3)// spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } + else if(pSpine_PointIdx == 0)// special case: first point + { + if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } + else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) + { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if(!found) tvec.Set(0, 0, 1); + }// if(pSpine_Closed) else + }// else if(pSpine_PointIdx == 0) + else if(pSpine_PointIdx == spine_idx_last)// special case: last point + { + if(pSpine_Closed) + {// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + else + {// vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } + else// regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// +void X3DImporter::readExtrusion(XmlNode &node) { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector crossSection; + bool endCap = true; + std::vector orientation; + std::vector scale; + bool solid = true; + std::vector spine; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "beginCap", beginCap); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "convex", convex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getVector2DArrayAttribute(node, "crossSection", crossSection); + XmlParser::getBoolAttribute(node, "endCap", endCap); + X3DXmlHelper::getFloatArrayAttribute(node, "orientation", orientation); + X3DXmlHelper::getVector2DArrayAttribute(node, "scale", scale); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getVector3DArrayAttribute(node, "spine", spine); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Extrusion, ne); + } + else + { + // + // check if default values must be assigned + // + if(spine.size() == 0) + { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } + else if(spine.size() == 1) + { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if(crossSection.size() == 0) + { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + {// orientation + size_t ori_size = orientation.size() / 4; + + if(ori_size < spine.size()) + { + float add_ori[4];// values that will be added + + if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } + else// else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); + }// END: orientation + + {// scale + if(scale.size() < spine.size()) + { + aiVector2D add_sc; + + if(scale.size() == 1)// if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else// else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc); + } + }// END: scale + // + // create and if needed - define new geometry object. + // + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_Extrusion, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ext_alias = *((X3DNodeElementIndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed;// flag: true if spine curve is closed. + bool cross_closed;// flag: true if cross curve is closed. + std::vector basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector > pointset_arr;// array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if(spine_closed) + { + beginCap |= endCap; + endCap = false; + } + + {// 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + }// for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + }// END: 1. Calculate array of basises + + {// 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + }// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross;// store transferred point set + }// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + }// END: 2. Create array of point sets. + + {// 3. Create CoordIdx. + // add caps if needed + if(beginCap) + { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + if(endCap) + { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + // add quads + for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) + { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col;// hold index basis for points of quad placed in right column; + + if(spi != spi_e) + right_col = spi + 1; + else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for(size_t cri = 0; cri < cr_sz; cri++) + { + if(cri != cr_last) + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + cri + 1), + static_cast(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + 0), + static_cast(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + }// for(size_t cri = 0; cri < cr_sz; cri++) + }// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + }// END: 3. Create CoordIdx. + + {// 4. Create vertices list. + // just copy all vertices + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + ext_alias.Vertices.push_back(pointset_arr[spi][cri]); + } + } + }// END: 4. Create vertices list. +//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); +//PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Extrusion"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedFaceSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + std::vector colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector coordIndex; + float creaseAngle = 0; + std::vector normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector texCoordIndex; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "convex", convex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getInt32ArrayAttribute(node, "normalIndex", normalIndex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getInt32ArrayAttribute(node, "texCoordIndex", texCoordIndex); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedFaceSet, ne); + } + else + { + // check data + if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedFaceSet, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") readCoordinate(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("IndexedFaceSet", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readSphere(XmlNode &node) { + std::string use, def; + ai_real radius = 1; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Sphere, ne); + } + else + { + const unsigned int tess = 3;///TODO: IME tessellation factor through ai_property + + std::vector tlist; + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Sphere, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for(std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) + { + ((X3DNodeElementGeometry3D *)ne)->Vertices.push_back(*it * radius); + } + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Sphere"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Group.cpp b/code/AssetLib/X3D/X3DImporter_Group.cpp new file mode 100644 index 000000000..d672d741b --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Group.cpp @@ -0,0 +1,273 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Group.cpp +/// \brief Parsing data from nodes of "Grouping" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform. +void X3DImporter::startReadGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::startReadStaticGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadStaticGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::startReadSwitch(XmlNode &node) { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getIntAttribute(node, "whichChoice", whichChoice); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + // also set values specific to this type of group + ((X3DNodeElementGroup *)mNodeElementCur)->UseChoice = true; + ((X3DNodeElementGroup *)mNodeElementCur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadSwitch() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::startReadTransform(XmlNode &node) { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "center", center); + X3DXmlHelper::getVector3DAttribute(node, "scale", scale); + X3DXmlHelper::getVector3DAttribute(node, "translation", translation); + std::vector tvec; + if (X3DXmlHelper::getFloatArrayAttribute(node, "rotation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); + memcpy(rotation, tvec.data(), sizeof(rotation)); + tvec.clear(); + } + if (X3DXmlHelper::getFloatArrayAttribute(node, "scaleOrientation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); + memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + tvec.clear(); + } + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + mNodeElementCur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((X3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::endReadTransform() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Light.cpp b/code/AssetLib/X3D/X3DImporter_Light.cpp new file mode 100644 index 000000000..b9bd17164 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Light.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Light.cpp +/// \brief Parsing data from nodes of "Lighting" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include + +namespace Assimp { + +// +void X3DImporter::readDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + XmlParser::getBoolAttribute(node, "on", on); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_DirectionalLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + ai_to_string((size_t)ne); // make random name + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "DirectionalLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readPointLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "PointLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readSpotLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + XmlParser::getFloatAttribute(node, "beamWidth", beamWidth); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getFloatAttribute(node, "cutOffAngle", cutOffAngle); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + if (beamWidth > cutOffAngle) beamWidth = cutOffAngle; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "SpotLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Macro.hpp b/code/AssetLib/X3D/X3DImporter_Macro.hpp new file mode 100644 index 000000000..8d902b346 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Macro.hpp @@ -0,0 +1,110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Macro.hpp +/// \brief Useful macrodefines. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED +#define X3DIMPORTER_MACRO_HPP_INCLUDED + +/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE) +/// Used for regular checking while attribute "USE" is defined. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF - string holding "DEF" value. +/// \param [in] pUSE - string holding "USE" value. +/// \param [in] pType - type of element to find. +/// \param [out] pNE - pointer to found node element. +#define MACRO_USE_CHECKANDAPPLY(pNode, pDEF, pUSE, pType, pNE) \ + do { \ + checkNodeMustBeEmpty(pNode); \ + if (!pDEF.empty()) Throw_DEF_And_USE(pNode.name()); \ + if (!FindNodeElement(pUSE, X3DElemType::pType, &pNE)) Throw_USE_NotFound(pNode.name(), pUSE); \ + mNodeElementCur->Children.push_back(pNE); /* add found object as child to current element */ \ + } while (false) + +/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET +/// Compact variant for checking "USE" and "DEF". +/// \param [in] pNode - pugi xml node to read. +/// \param [out] pDEF_Var - output variable name for "DEF" value. +/// \param [out] pUSE_Var - output variable name for "USE" value. +#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pNode, pDEF_Var, pUSE_Var) \ + do { \ + XmlParser::getStdStrAttribute(pNode, "def", pDEF_Var); \ + XmlParser::getStdStrAttribute(pNode, "use", pUSE_Var); \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pP1); \ + pOut.push_back(pP2); \ + pOut.push_back(pP3); \ + pOut.push_back(pP4); \ + } else { \ + pOut.push_back(pP4); \ + pOut.push_back(pP3); \ + pOut.push_back(pP2); \ + pOut.push_back(pP1); \ + } \ + } while (false) + +#endif // X3DIMPORTER_MACRO_HPP_INCLUDED diff --git a/code/AssetLib/X3D/X3DImporter_Metadata.cpp b/code/AssetLib/X3D/X3DImporter_Metadata.cpp new file mode 100644 index 000000000..bb1ba9a9d --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Metadata.cpp @@ -0,0 +1,255 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Metadata.cpp +/// \brief Parsing data from nodes of "Metadata" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +bool X3DImporter::checkForMetadataNode(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node); + } else if (name == "MetadataDouble") { + readMetadataDouble(node); + } else if (name == "MetadataFloat") { + readMetadataFloat(node); + } else if (name == "MetadataInteger") { + readMetadataInteger(node); + } else if (name == "MetadataSet") { + readMetadataSet(node); + } else if (name == "MetadataString") { + readMetadataString(node); + } else + return false; + return true; +} + +void X3DImporter::childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName) { + ParseHelper_Node_Enter(pParentElement); + for (auto childNode : node.children()) { + if (!checkForMetadataNode(childNode)) skipUnsupportedNode(pNodeName, childNode); + } + ParseHelper_Node_Exit(); +} + +/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName) +/// Find element by "USE" or create new one. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF_Var - variable name with "DEF" value. +/// \param [in] pUSE_Var - variable name with "USE" value. +/// \param [in] pReference - variable name with "reference" value. +/// \param [in] pValue - variable name with "value" value. +/// \param [in, out] pNE - pointer to node element. +/// \param [in] pMetaClass - Class of node. +/// \param [in] pMetaName - Name of node. +/// \param [in] pType - type of element to find. +#define MACRO_METADATA_FINDCREATE(pNode, pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \ + /* if "USE" defined then find already defined element. */ \ + if (!pUSE_Var.empty()) { \ + MACRO_USE_CHECKANDAPPLY(pNode, pDEF_Var, pUSE_Var, pType, pNE); \ + } else { \ + pNE = new pMetaClass(mNodeElementCur); \ + if (!pDEF_Var.empty()) pNE->ID = pDEF_Var; \ + \ + ((pMetaClass *)pNE)->Reference = pReference; \ + ((pMetaClass *)pNE)->Value = pValue; \ + /* also metadata node can contain childs */ \ + if (!isNodeEmpty(pNode)) \ + childrenReadMetadata(pNode, pNE, pMetaName); /* in that case node element will be added to child elements list of current node. */ \ + else \ + mNodeElementCur->Children.push_back(pNE); /* else - add element to child list manually */ \ + \ + NodeElement_List.push_back(pNE); /* add new element to elements list. */ \ + } /* if(!pUSE_Var.empty()) else */ \ + \ + do { \ + } while (false) + +// +void X3DImporter::readMetadataBoolean(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getBooleanArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaBoolean, "MetadataBoolean", ENET_MetaBoolean); +} + +// +void X3DImporter::readMetadataDouble(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getDoubleArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaDouble, "MetadataDouble", ENET_MetaDouble); +} + +// +void X3DImporter::readMetadataFloat(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getFloatArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaFloat, "MetadataFloat", ENET_MetaFloat); +} + +// +void X3DImporter::readMetadataInteger(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getInt32ArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaInt, "MetadataInteger", ENET_MetaInteger); +} + +// +void X3DImporter::readMetadataSet(XmlNode &node) { + std::string def, use; + std::string name, reference; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_MetaSet, ne); + } else { + ne = new X3DNodeElementMetaSet(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementMetaSet *)ne)->Reference = reference; + // also metadata node can contain childs + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "MetadataSet"); + else + mNodeElementCur->Children.push_back(ne); // made object as child to current element + + NodeElement_List.push_back(ne); // add new element to elements list. + } // if(!use.empty()) else +} + +// +void X3DImporter::readMetadataString(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getStringArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaString, "MetadataString", ENET_MetaString); +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Networking.cpp b/code/AssetLib/X3D/X3DImporter_Networking.cpp new file mode 100644 index 000000000..d5ef58397 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Networking.cpp @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Networking.cpp +/// \brief Parsing data from nodes of "Networking" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +// Header files, Assimp. +#include + +//#include + +namespace Assimp { + +//static std::regex pattern_parentDir(R"((^|/)[^/]+/../)"); +static std::string parentDir("/../"); + +// +void X3DImporter::readInline(XmlNode &node) { + std::string def, use; + bool load = true; + std::list url; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "load", load); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + if (load && !url.empty()) { + std::string full_path = mpIOHandler->CurrentDirectory() + url.front(); + + //full_path = std::regex_replace(full_path, pattern_parentDir, "$1"); + for (std::string::size_type pos = full_path.find(parentDir); pos != std::string::npos; pos = full_path.find(parentDir, pos)) { + if (pos > 0) { + std::string::size_type pos2 = full_path.rfind('/', pos - 1); + if (pos2 != std::string::npos) { + full_path.erase(pos2, pos - pos2 + 3); + pos = pos2; + } else { + full_path.erase(0, pos + 4); + pos = 0; + } + } else { + pos += 3; + } + } + // Attribute "url" can contain list of strings. But we need only one - first. + std::string::size_type slashPos = full_path.find_last_of("\\/"); + mpIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : full_path.substr(0, slashPos + 1)); + ParseFile(full_path, mpIOHandler); + mpIOHandler->PopDirectory(); + } + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) childrenReadMetadata(node, mNodeElementCur, "Inline"); + + // exit from node in that place + ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Node.hpp b/code/AssetLib/X3D/X3DImporter_Node.hpp new file mode 100644 index 000000000..8079cbf11 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Node.hpp @@ -0,0 +1,459 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Node.hpp +/// \brief Elements of scene graph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H +#define INCLUDED_AI_X3D_IMPORTER_NODE_H + +// Header files, Assimp. +#include + +#include +#include + +enum X3DElemType { + ENET_Group, ///< Element has type "Group". + ENET_MetaBoolean, ///< Element has type "Metadata boolean". + ENET_MetaDouble, ///< Element has type "Metadata double". + ENET_MetaFloat, ///< Element has type "Metadata float". + ENET_MetaInteger, ///< Element has type "Metadata integer". + ENET_MetaSet, ///< Element has type "Metadata set". + ENET_MetaString, ///< Element has type "Metadata string". + ENET_Arc2D, ///< Element has type "Arc2D". + ENET_ArcClose2D, ///< Element has type "ArcClose2D". + ENET_Circle2D, ///< Element has type "Circle2D". + ENET_Disk2D, ///< Element has type "Disk2D". + ENET_Polyline2D, ///< Element has type "Polyline2D". + ENET_Polypoint2D, ///< Element has type "Polypoint2D". + ENET_Rectangle2D, ///< Element has type "Rectangle2D". + ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". + ENET_Box, ///< Element has type "Box". + ENET_Cone, ///< Element has type "Cone". + ENET_Cylinder, ///< Element has type "Cylinder". + ENET_Sphere, ///< Element has type "Sphere". + ENET_ElevationGrid, ///< Element has type "ElevationGrid". + ENET_Extrusion, ///< Element has type "Extrusion". + ENET_Coordinate, ///< Element has type "Coordinate". + ENET_Normal, ///< Element has type "Normal". + ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". + ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". + ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". + ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". + ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". + ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". + ENET_LineSet, ///< Element has type "LineSet". + ENET_PointSet, ///< Element has type "PointSet". + ENET_TriangleSet, ///< Element has type "TriangleSet". + ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". + ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". + ENET_Color, ///< Element has type "Color". + ENET_ColorRGBA, ///< Element has type "ColorRGBA". + ENET_Shape, ///< Element has type "Shape". + ENET_Appearance, ///< Element has type "Appearance". + ENET_Material, ///< Element has type "Material". + ENET_ImageTexture, ///< Element has type "ImageTexture". + ENET_TextureTransform, ///< Element has type "TextureTransform". + ENET_DirectionalLight, ///< Element has type "DirectionalLight". + ENET_PointLight, ///< Element has type "PointLight". + ENET_SpotLight, ///< Element has type "SpotLight". + + ENET_Invalid ///< Element has invalid type and possible contain invalid data. +}; + +struct X3DNodeElementBase { + X3DNodeElementBase *Parent; + std::string ID; + std::list Children; + X3DElemType Type; + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Type(type), Parent(pParent) { + // empty + } +}; + +/// This struct hold value. +struct X3DNodeElementColor : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColor(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct X3DNodeElementColor + +/// This struct hold value. +struct X3DNodeElementColorRGBA : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct X3DNodeElementColorRGBA + +/// This struct hold value. +struct X3DNodeElementCoordinate : public X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct X3DNodeElementCoordinate + +/// This struct hold value. +struct X3DNodeElementNormal : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementNormal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct X3DNodeElementNormal + +/// This struct hold value. +struct X3DNodeElementTextureCoordinate : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct X3DNodeElementTextureCoordinate + +/// Two-dimensional figure. +struct X3DNodeElementGeometry2D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class X3DNodeElementGeometry2D + +/// Three-dimensional body. +struct X3DNodeElementGeometry3D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class X3DNodeElementGeometry3D + +/// Uniform rectangular grid of varying height. +struct X3DNodeElementElevationGrid : X3DNodeElementGeometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with indexed vertices. +struct X3DNodeElementIndexedSet : public X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementIndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with set of vertices. +struct X3DNodeElementSet : X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementSet(X3DElemType type, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(type, pParent) {} + +}; // class X3DNodeElementSet + +/// This struct hold value. +struct X3DNodeElementShape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementShape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct X3DNodeElementShape + +/// This struct hold value. +struct X3DNodeElementAppearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementAppearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct X3DNodeElementAppearance + +struct X3DNodeElementMaterial : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementMaterial(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class X3DNodeElementMaterial + +/// This struct hold value. +struct X3DNodeElementImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct X3DNodeElementImageTexture + +/// This struct hold value. +struct X3DNodeElementTextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct X3DNodeElementTextureTransform + +struct X3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + X3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } +}; + +/// \struct X3DNodeElementLight +/// This struct hold value. +struct X3DNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct X3DNodeElementLight + +#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H diff --git a/code/AssetLib/X3D/X3DImporter_Postprocess.cpp b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp new file mode 100644 index 000000000..24e03ca16 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp @@ -0,0 +1,731 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Postprocess.cpp +/// \brief Convert built scenegraph and objects to Assimp scenegraph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +// Header files, Assimp. +#include +#include +#include + +// Header files, stdlib. +#include +#include +#include + +namespace Assimp { + +aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const { + X3DNodeElementBase *cur_node; + std::list matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = mNodeElementCur; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DElemType::ENET_Group) matr.push_back(((X3DNodeElementGroup *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } while (cur_node != nullptr); + } + + // multiplicate all matrices in reverse order + for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) + out_matr = out_matr * (*rit); + + return out_matr; +} + +void X3DImporter::PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const { + // walk through childs and find for metadata. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } else { + return false; + } +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const { + const X3DNodeElementLight &ne = *((X3DNodeElementLight *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case X3DElemType::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case X3DElemType::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case X3DElemType::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + ai_to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to node. Walk through childs and add all stored data. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if ((*el_it)->Type == X3DElemType::ENET_Material) { + aiColor3D tcol3; + float tvalf; + X3DNodeElementMaterial &tnemat = *((X3DNodeElementMaterial *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == X3DElemType::ENET_Material) + else if ((*el_it)->Type == X3DElemType::ENET_ImageTexture) { + X3DNodeElementImageTexture &tnetex = *((X3DNodeElementImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_ImageTexture) + else if ((*el_it)->Type == X3DElemType::ENET_TextureTransform) { + aiUVTransform trans; + X3DNodeElementTextureTransform &tnetextr = *((X3DNodeElementTextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_TextureTransform) + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == X3DElemType::ENET_Arc2D) || (pNodeElement.Type == X3DElemType::ENET_ArcClose2D) || + (pNodeElement.Type == X3DElemType::ENET_Circle2D) || (pNodeElement.Type == X3DElemType::ENET_Disk2D) || + (pNodeElement.Type == X3DElemType::ENET_Polyline2D) || (pNodeElement.Type == X3DElemType::ENET_Polypoint2D) || + (pNodeElement.Type == X3DElemType::ENET_Rectangle2D) || (pNodeElement.Type == X3DElemType::ENET_TriangleSet2D)) { + X3DNodeElementGeometry2D &tnemesh = *((X3DNodeElementGeometry2D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == X3DElemType::ENET_Box) || (pNodeElement.Type == X3DElemType::ENET_Cone) || + (pNodeElement.Type == X3DElemType::ENET_Cylinder) || (pNodeElement.Type == X3DElemType::ENET_Sphere)) { + X3DNodeElementGeometry3D &tnemesh = *((X3DNodeElementGeometry3D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == X3DElemType::ENET_ElevationGrid) { + X3DNodeElementElevationGrid &tnemesh = *((X3DNodeElementElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, ((X3DNodeElementNormal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) + + if (pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) + + if ((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == X3DElemType::ENET_Extrusion) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_PointSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_PointSet) + + if (pNodeElement.Type == X3DElemType::ENET_LineSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_LineSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + ai_to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const { + std::list::const_iterator chit_begin = pNodeElement.Children.begin(); + std::list::const_iterator chit_end = pNodeElement.Children.end(); + std::list SceneNode_Child; + std::list SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const X3DNodeElementGroup &tne_group = *((X3DNodeElementGroup *)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == X3DElemType::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((X3DNodeElementShape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DNodeElementLight *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + ai_to_string((*it)->Type) + "."); + } + } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const { + std::list meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); + meta_idx = 0; + for (std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + X3DNodeElementMeta *cur_meta = (X3DNodeElementMeta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == X3DElemType::ENET_MetaBoolean) { + if (((X3DNodeElementMetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaBoolean *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaDouble) { + if (((X3DNodeElementMetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((X3DNodeElementMetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaFloat) { + if (((X3DNodeElementMetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaInteger) { + if (((X3DNodeElementMetaInt *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaInt *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaString) { + if (((X3DNodeElementMetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((X3DNodeElementMetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == X3DElemType::ENET_Meta*) else + } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Rendering.cpp b/code/AssetLib/X3D/X3DImporter_Rendering.cpp new file mode 100644 index 000000000..fe7e68081 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Rendering.cpp @@ -0,0 +1,987 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Rendering.cpp +/// \brief Parsing data from nodes of "Rendering" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +void X3DImporter::readColor(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor3DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColor(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColor *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Color"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readColorRGBA(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor4DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColorRGBA(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColorRGBA *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ColorRGBA"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Coordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedLineSet(XmlNode &node) { + std::string use, def; + std::vector colorIndex; + bool colorPerVertex = true; + std::vector coordIndex; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne); + } else { + // check data + if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3))) + throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.CoordIndex = coordIndex; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for Color and Coordinate nodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedLineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + idx[1] = idx[2]; + } else { + idx[counter] = idx[2]; + } + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[counter++] = *idx_it; + if (counter > 2) { + counter = 0; + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + idx[counter & 1] = idx[2]; + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readLineSet(XmlNode &node) { + std::string use, def; + std::vector vertexCount; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne); + } else { + // check data + if (vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.VertexCount = vertexCount; + // create CoordIdx + size_t coord_num = 0; + + ne_alias.CoordIndex.clear(); + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two."); + + for (int32_t i = 0; i < *vc_it; i++) + ne_alias.CoordIndex.push_back(static_cast(coord_num++)); // add vertices indices + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("LineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readPointSet(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("PointSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector fanCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne); + } else { + // check data + if (fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = fanCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num_first, coord_num_prev; + + ne_alias.CoordIndex.clear(); + // assign indices for first triangle + coord_num_first = 0; + coord_num_prev = 1; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three."); + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 2 1 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev)); + } else { + // 1 2 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev + 1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } // for(int32_t vc = 2; vc < *vc_it; vc++) + + coord_num_prev++; // that index will be center of next fan + coord_num_first = coord_num_prev++; // forward to next point - second point of fan + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector stripCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne); + } else { + // check data + if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = stripCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num0, coord_num1, coord_num2; // indices of current triangle + bool odd_tri; // sequence of current triangle + size_t coord_num_sb; // index of first point of strip + + ne_alias.CoordIndex.clear(); + coord_num_sb = 0; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three."); + + // set initial values for first triangle + coord_num0 = coord_num_sb; + coord_num1 = coord_num_sb + 1; + coord_num2 = coord_num_sb + 2; + odd_tri = true; + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 0 2 + // 1 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + } else { + // 0 1 + // 2 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + // prepare values for next triangle + if (odd_tri) { + coord_num0 = coord_num2; + coord_num2++; + } else { + coord_num1 = coord_num2; + coord_num2 = coord_num1 + 1; + } + + odd_tri = !odd_tri; + coord_num_sb = coord_num2; // that index will be start of next strip + } // for(int32_t vc = 2; vc < *vc_it; vc++) + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readNormal(XmlNode &node) { + std::string use, def; + std::list vector; + X3DNodeElementBase *ne; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "vector", vector); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementNormal(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementNormal *)ne)->Value = vector; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Normal"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Shape.cpp b/code/AssetLib/X3D/X3DImporter_Shape.cpp new file mode 100644 index 000000000..7a292be5c --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Shape.cpp @@ -0,0 +1,221 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Shape.cpp +/// \brief Parsing data from nodes of "Shape" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +void X3DImporter::readShape(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Shape, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementShape(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for appearance node + if (currentChildName == "Appearance") readAppearance(currentChildNode); + // check for X3DGeometryNodes + else if (currentChildName == "Arc2D") readArc2D(currentChildNode); + else if (currentChildName == "ArcClose2D") readArcClose2D(currentChildNode); + else if (currentChildName == "Circle2D") readCircle2D(currentChildNode); + else if (currentChildName == "Disk2D") readDisk2D(currentChildNode); + else if (currentChildName == "Polyline2D") readPolyline2D(currentChildNode); + else if (currentChildName == "Polypoint2D") readPolypoint2D(currentChildNode); + else if (currentChildName == "Rectangle2D") readRectangle2D(currentChildNode); + else if (currentChildName == "TriangleSet2D") readTriangleSet2D(currentChildNode); + else if (currentChildName == "Box") readBox(currentChildNode); + else if (currentChildName == "Cone") readCone(currentChildNode); + else if (currentChildName == "Cylinder") readCylinder(currentChildNode); + else if (currentChildName == "ElevationGrid") readElevationGrid(currentChildNode); + else if (currentChildName == "Extrusion") readExtrusion(currentChildNode); + else if (currentChildName == "IndexedFaceSet") readIndexedFaceSet(currentChildNode); + else if (currentChildName == "Sphere") readSphere(currentChildNode); + else if (currentChildName == "IndexedLineSet") readIndexedLineSet(currentChildNode); + else if (currentChildName == "LineSet") readLineSet(currentChildNode); + else if (currentChildName == "PointSet") readPointSet(currentChildNode); + else if (currentChildName == "IndexedTriangleFanSet") readIndexedTriangleFanSet(currentChildNode); + else if (currentChildName == "IndexedTriangleSet") readIndexedTriangleSet(currentChildNode); + else if (currentChildName == "IndexedTriangleStripSet") readIndexedTriangleStripSet(currentChildNode); + else if (currentChildName == "TriangleFanSet") readTriangleFanSet(currentChildNode); + else if (currentChildName == "TriangleSet") readTriangleSet(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Shape", currentChildNode); + } + + ParseHelper_Node_Exit(); + } // if (!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and +// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader, +// PackagedShader, ProgramShader). +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model." +// +void X3DImporter::readAppearance(XmlNode &node) { + std::string use, def; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Appearance, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementAppearance(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + if (currentChildName == "Material") readMaterial(currentChildNode); + else if (currentChildName == "ImageTexture") readImageTexture(currentChildNode); + else if (currentChildName == "TextureTransform") readTextureTransform(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Appearance", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readMaterial(XmlNode &node) { + std::string use, def; + float ambientIntensity = 0.2f; + float shininess = 0.2f; + float transparency = 0; + aiColor3D diffuseColor(0.8f, 0.8f, 0.8f); + aiColor3D emissiveColor(0, 0, 0); + aiColor3D specularColor(0, 0, 0); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + XmlParser::getFloatAttribute(node, "shininess", shininess); + XmlParser::getFloatAttribute(node, "transparency", transparency); + X3DXmlHelper::getColor3DAttribute(node, "diffuseColor", diffuseColor); + X3DXmlHelper::getColor3DAttribute(node, "emissiveColor", emissiveColor); + X3DXmlHelper::getColor3DAttribute(node, "specularColor", specularColor); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Material, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementMaterial(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + ((X3DNodeElementMaterial *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementMaterial *)ne)->Shininess = shininess; + ((X3DNodeElementMaterial *)ne)->Transparency = transparency; + ((X3DNodeElementMaterial *)ne)->DiffuseColor = diffuseColor; + ((X3DNodeElementMaterial *)ne)->EmissiveColor = emissiveColor; + ((X3DNodeElementMaterial *)ne)->SpecularColor = specularColor; + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Material"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Texturing.cpp b/code/AssetLib/X3D/X3DImporter_Texturing.cpp new file mode 100644 index 000000000..6463e2808 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Texturing.cpp @@ -0,0 +1,179 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Texturing.cpp +/// \brief Parsing data from nodes of "Texturing" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// When the url field contains no values ([]), texturing is disabled. +void X3DImporter::readImageTexture(XmlNode &node) { + std::string use, def; + bool repeatS = true; + bool repeatT = true; + std::list url; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "repeatS", repeatS); + XmlParser::getBoolAttribute(node, "repeatT", repeatT); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ImageTexture, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementImageTexture(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementImageTexture *)ne)->RepeatS = repeatS; + ((X3DNodeElementImageTexture *)ne)->RepeatT = repeatT; + // Attribute "url" can contain list of strings. But we need only one - first. + if (!url.empty()) + ((X3DNodeElementImageTexture *)ne)->URL = url.front(); + else + ((X3DNodeElementImageTexture *)ne)->URL = ""; + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ImageTexture"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureCoordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureCoordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureTransform(XmlNode &node) { + std::string use, def; + aiVector2D center(0, 0); + float rotation = 0; + aiVector2D scale(1, 1); + aiVector2D translation(0, 0); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "center", center); + XmlParser::getFloatAttribute(node, "rotation", rotation); + X3DXmlHelper::getVector2DAttribute(node, "scale", scale); + X3DXmlHelper::getVector2DAttribute(node, "translation", translation); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureTransform, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureTransform(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureTransform *)ne)->Center = center; + ((X3DNodeElementTextureTransform *)ne)->Rotation = rotation; + ((X3DNodeElementTextureTransform *)ne)->Scale = scale; + ((X3DNodeElementTextureTransform *)ne)->Translation = translation; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureTransform"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DXmlHelper.cpp b/code/AssetLib/X3D/X3DXmlHelper.cpp new file mode 100644 index 000000000..ff24b74b3 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.cpp @@ -0,0 +1,294 @@ +#include "X3DXmlHelper.h" +#include "X3DImporter.hpp" + +#include + +namespace Assimp { + +bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.r = stof(*it++); + color.g = stof(*it++); + color.b = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 2) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it++); + color.z = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + boolArray.push_back(s[0] == 't' || s[0] == '1'); + else + Throw_ConvertFail_Str2ArrB(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + doubleArray.push_back(atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrD(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + floatArray.push_back((float)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + intArray.push_back((int32_t)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + std::string currentConcat = ""; + bool inQuotes = false; + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) { + if (inQuotes) { + if (*(s.rbegin()) == '"') { + stringList.push_back(currentConcat + s.substr(0, s.length() - 1)); + currentConcat = ""; + inQuotes = false; + } else { + currentConcat += " " + s; + } + } else { + if (s[0] == '"') { + currentConcat = s.substr(1); + inQuotes = true; + } else { + stringList.push_back(s); + } + } + } else if (!inQuotes) + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + if (inQuotes) Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + return true; + } + return false; +} + +bool X3DXmlHelper::getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray) { + std::list tlist; + + if (getStringListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + stringArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + stringArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 2) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector2D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector2DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector3D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + tvec.z = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector3DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor3D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 4 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor4D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + tvec.a = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DXmlHelper.h b/code/AssetLib/X3D/X3DXmlHelper.h new file mode 100644 index 000000000..dd305f883 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace Assimp { + +class X3DXmlHelper { +public: + static bool getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color); + static bool getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &vector); + static bool getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &vector); + + static bool getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray); + static bool getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray); + static bool getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray); + static bool getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray); + static bool getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringArray); + static bool getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray); + + static bool getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); + static bool getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); +}; + +} // namespace Assimp diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 933b5488c..c99b81558 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -800,9 +800,23 @@ ADD_ASSIMP_IMPORTER( X ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DImporter.cpp + AssetLib/X3D/X3DImporter_Geometry2D.cpp + AssetLib/X3D/X3DImporter_Geometry3D.cpp + AssetLib/X3D/X3DImporter_Group.cpp + AssetLib/X3D/X3DImporter_Light.cpp + AssetLib/X3D/X3DImporter_Metadata.cpp + AssetLib/X3D/X3DImporter_Networking.cpp + AssetLib/X3D/X3DImporter_Postprocess.cpp + AssetLib/X3D/X3DImporter_Rendering.cpp + AssetLib/X3D/X3DImporter_Shape.cpp + AssetLib/X3D/X3DImporter_Texturing.cpp AssetLib/X3D/X3DImporter.hpp + AssetLib/X3D/X3DImporter_Macro.hpp + AssetLib/X3D/X3DImporter_Node.hpp AssetLib/X3D/X3DGeoHelper.cpp AssetLib/X3D/X3DGeoHelper.h + AssetLib/X3D/X3DXmlHelper.cpp + AssetLib/X3D/X3DXmlHelper.h ) ADD_ASSIMP_IMPORTER( GLTF diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index 5df096166..7500d2610 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -365,9 +365,7 @@ void GetImporterInstanceList(std::vector &out) { out.push_back(new D3MFImporter()); #endif #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - if (devImportersEnabled) { // https://github.com/assimp/assimp/issues/3647 - out.push_back(new X3DImporter()); - } + out.push_back(new X3DImporter()); #endif #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER out.push_back(new MMDImporter());