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