Parse the <build> node + refactoring

pull/3558/head
Jean-Louis 2020-12-28 12:31:54 +01:00
parent 629147043e
commit eda967bd02
2 changed files with 238 additions and 104 deletions

View File

@ -55,6 +55,8 @@ namespace XmlTag {
static const std::string resources = "resources";
static const std::string object = "object";
static const std::string mesh = "mesh";
static const std::string components = "components";
static const std::string component = "component";
static const std::string vertices = "vertices";
static const std::string vertex = "vertex";
static const std::string triangles = "triangles";

View File

@ -66,19 +66,73 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace D3MF {
enum class ResourceType {
RT_Object,
RT_BaseMaterials,
RT_Unknown
}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
class Resource
{
public:
Resource(int id) :
mId(id) {}
virtual ~Resource() {}
int mId;
virtual ResourceType getType() {
return ResourceType::RT_Unknown;
}
};
class BaseMaterials : public Resource {
public:
BaseMaterials(int id) :
Resource(id),
mMaterials(),
mMaterialIndex() {}
std::vector<aiMaterial *> mMaterials;
std::vector<unsigned int> mMaterialIndex;
virtual ResourceType getType() {
return ResourceType::RT_BaseMaterials;
}
};
class Object : public Resource {
public:
std::vector<aiMesh*> mMeshes;
std::vector<unsigned int> mMeshIndex;
std::vector<int> mComponents;
Object(int id) :
Resource(id) {}
virtual ResourceType getType() {
return ResourceType::RT_Object;
}
};
class XmlSerializer {
public:
XmlSerializer(XmlParser *xmlParser) :
mMeshes(),
mBasematerialsDictionnary(),
mResourcesDictionnary(),
mMaterialCount(0),
mMeshCount(0),
mXmlParser(xmlParser) {
// empty
}
~XmlSerializer() {
// empty
for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) {
delete it->second;
}
}
void ImportXml(aiScene *scene) {
@ -86,10 +140,8 @@ public:
return;
}
scene->mRootNode = new aiNode();
std::vector<aiNode *> children;
scene->mRootNode = new aiNode("3MF");
std::string nodeName;
XmlNode node = mXmlParser->getRootNode().child("model");
if (node.empty()) {
return;
@ -98,9 +150,7 @@ public:
for (XmlNode currentNode = resNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentNodeName = currentNode.name();
if (currentNodeName == D3MF::XmlTag::object) {
children.push_back(ReadObject(currentNode, scene));
} else if (currentNodeName == D3MF::XmlTag::build) {
//
ReadObject(currentNode);;
} else if (currentNodeName == D3MF::XmlTag::basematerials) {
ReadBaseMaterials(currentNode);
} else if (currentNodeName == D3MF::XmlTag::meta) {
@ -108,10 +158,75 @@ public:
}
}
if (scene->mRootNode->mName.length == 0) {
scene->mRootNode->mName.Set("3MF");
XmlNode buildNode = node.child("build");
for (XmlNode currentNode = buildNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentNodeName = currentNode.name();
if (currentNodeName == D3MF::XmlTag::item) {
int objectId = -1;
std::string transform;
getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transform);
aiNode *sceneNode = new aiNode("item");
auto it = mResourcesDictionnary.find(objectId);
if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
Object *obj = static_cast<Object *>(it->second);
std::vector<unsigned int> meshes;
getMeshesList(obj, meshes);
sceneNode->mNumMeshes = static_cast<unsigned int>(meshes.size());
sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
std::copy(meshes.begin(), meshes.end(), sceneNode->mMeshes);
if (hasTransform) {
// split the string
std::vector<float> numbers;
std::string currentNumber;
for (size_t i = 0; i < transform.size(); ++i) {
const char c = transform[i];
if (c == ' ') {
if (currentNumber.size() > 0) {
float f = std::stof(currentNumber);
numbers.push_back(f);
currentNumber.clear();
}
} else {
currentNumber.push_back(c);
}
}
if (currentNumber.size() > 0) {
float f = std::stof(currentNumber);
numbers.push_back(f);
}
aiMatrix4x4 transformMatrix;
transformMatrix.a1 = numbers[0];
transformMatrix.b1 = numbers[1];
transformMatrix.c1 = numbers[2];
transformMatrix.d1 = 0;
transformMatrix.a2 = numbers[3];
transformMatrix.b2 = numbers[4];
transformMatrix.c2 = numbers[5];
transformMatrix.d2 = 0;
transformMatrix.a3 = numbers[6];
transformMatrix.b3 = numbers[7];
transformMatrix.c3 = numbers[8];
transformMatrix.d3 = 0;
transformMatrix.a4 = numbers[9];
transformMatrix.b4 = numbers[10];
transformMatrix.c4 = numbers[11];
transformMatrix.d4 = 1;
sceneNode->mTransformation = transformMatrix;
}
scene->mRootNode->addChildren(1, &sceneNode);
}
}
}
// import the metadata
if (!mMetaData.empty()) {
const size_t numMeta(mMetaData.size());
@ -131,17 +246,15 @@ public:
scene->mNumMaterials = static_cast<unsigned int>(mMaterialCount);
if (0 != scene->mNumMaterials) {
scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
for (auto it = mBasematerialsDictionnary.begin(); it != mBasematerialsDictionnary.end(); it++) {
for (unsigned int i = 0; i < it->second.size(); ++i) {
scene->mMaterials[it->second[i].first] = it->second[i].second;
for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) {
if (it->second->getType() == ResourceType::RT_BaseMaterials) {
BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) {
scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i];
}
}
}
}
// create the scene-graph
scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren]();
std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
}
private:
@ -156,53 +269,79 @@ private:
}
}
aiNode *ReadObject(XmlNode &node, aiScene *scene) {
std::unique_ptr<aiNode> nodePtr(new aiNode());
bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
std::string strValue;
bool ret = getNodeAttribute(node, attribute, strValue);
if (ret) {
value = std::atoi(strValue.c_str());
return true;
} else {
return false;
}
}
std::vector<unsigned long> meshIds;
void getMeshesList(Object *obj, std::vector<unsigned int> &meshes) {
for (size_t i = 0; i < obj->mMeshIndex.size(); ++i) {
meshes.push_back(obj->mMeshIndex[i]);
}
for (size_t i = 0; i < obj->mComponents.size(); ++i) {
int componentId = obj->mComponents[i];
auto it = mResourcesDictionnary.find(componentId);
if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object)
getMeshesList(static_cast<Object*>(it->second), meshes);
}
}
std::string id, type, pid, pindex;
void ReadObject(XmlNode &node) {
int id = -1, pid = -1, pindex = -1;
bool hasId = getNodeAttribute(node, D3MF::XmlTag::id, id);
//bool hasType = getNodeAttribute(node, D3MF::XmlTag::type, type); not used currently
bool hasPid = getNodeAttribute(node, D3MF::XmlTag::pid, pid);
bool hasPindex = getNodeAttribute(node, D3MF::XmlTag::pindex, pindex);
std::string idStr = to_string(id);
if (!hasId) {
return nullptr;
return;
}
nodePtr->mParent = scene->mRootNode;
nodePtr->mName.Set(id);
size_t meshIdx = mMeshes.size();
Object *obj = new Object(id);
for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentName = currentNode.name();
if (currentName == D3MF::XmlTag::mesh) {
auto mesh = ReadMesh(currentNode);
mesh->mName.Set(id);
if (hasPid && hasPindex && mBasematerialsDictionnary.find(atoi(pid.c_str())) != mBasematerialsDictionnary.end()) {
int iPid = atoi(pid.c_str());
int iPindex = atoi(pindex.c_str());
mesh->mMaterialIndex = mBasematerialsDictionnary[iPid][iPindex].first;
mesh->mName.Set(idStr);
if (hasPid) {
auto it = mResourcesDictionnary.find(pid);
if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
}
}
mMeshes.push_back(mesh);
meshIds.push_back(static_cast<unsigned long>(meshIdx));
++meshIdx;
obj->mMeshes.push_back(mesh);
obj->mMeshIndex.push_back(mMeshCount);
mMeshCount++;
} else if (currentName == D3MF::XmlTag::components) {
for (XmlNode currentSubNode = currentNode.first_child(); currentSubNode; currentSubNode = currentSubNode.next_sibling()) {
if (currentSubNode.name() == D3MF::XmlTag::component) {
int objectId = -1;
if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId))
obj->mComponents.push_back(objectId);
}
}
}
}
nodePtr->mNumMeshes = static_cast<unsigned int>(meshIds.size());
nodePtr->mMeshes = new unsigned int[nodePtr->mNumMeshes];
std::copy(meshIds.begin(), meshIds.end(), nodePtr->mMeshes);
return nodePtr.release();
mResourcesDictionnary.insert(std::make_pair(id, obj));
}
aiMesh *ReadMesh(XmlNode &node) {
aiMesh *mesh = new aiMesh();
for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentName = currentNode.name();
if (currentName == D3MF::XmlTag::vertices) {
@ -241,6 +380,7 @@ private:
mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
}
@ -258,14 +398,23 @@ private:
for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
const std::string &currentName = currentNode.name();
if (currentName == D3MF::XmlTag::triangle) {
faces.push_back(ReadTriangle(currentNode));
const char *pidToken = currentNode.attribute(D3MF::XmlTag::pid.c_str()).as_string();
const char *p1Token = currentNode.attribute(D3MF::XmlTag::p1.c_str()).as_string();
if (nullptr != pidToken && nullptr != p1Token && mBasematerialsDictionnary.find(std::atoi(pidToken)) != mBasematerialsDictionnary.end()) {
int pid(std::atoi(pidToken));
int p1(std::atoi(p1Token));
mesh->mMaterialIndex = mBasematerialsDictionnary[pid][p1].first;
// TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material
aiFace face = ReadTriangle(currentNode);
faces.push_back(face);
int pid, p1;
bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
if (hasPid && hasP1) {
auto it = mResourcesDictionnary.find(pid);
if (it != mResourcesDictionnary.end())
{
if (it->second->getType() == ResourceType::RT_BaseMaterials) {
BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
}
// TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material
}
}
}
}
@ -290,21 +439,20 @@ private:
}
void ReadBaseMaterials(XmlNode &node) {
std::vector<unsigned int> MatIdArray;
const char *baseMaterialId = node.attribute(D3MF::XmlTag::basematerials_id.c_str()).as_string();
if (nullptr != baseMaterialId) {
unsigned int id = std::atoi(baseMaterialId);
std::vector<std::pair<unsigned int, aiMaterial *> > materials;
int id = -1;
if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) {
BaseMaterials* baseMaterials = new BaseMaterials(id);
for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling())
{
if (currentNode.name() == D3MF::XmlTag::basematerials_base) {
materials.push_back(std::make_pair(mMaterialCount, readMaterialDef(currentNode, id)));
baseMaterials->mMaterialIndex.push_back(mMaterialCount);
baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id));
mMaterialCount++;
}
}
mBasematerialsDictionnary.insert(std::make_pair(id, materials));
mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
}
}
@ -320,37 +468,24 @@ private:
}
const char *buf(color);
if ('#' != *buf) {
if ('#' != buf[0]) {
return false;
}
++buf;
char comp[3] = { 0, 0, '\0' };
comp[0] = *buf;
++buf;
comp[1] = *buf;
++buf;
diffuse.r = static_cast<ai_real>(strtol(comp, nullptr, 16)) / ai_real(255.0);
char r[3] = { buf[1], buf[2], '\0' };
diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
comp[0] = *buf;
++buf;
comp[1] = *buf;
++buf;
diffuse.g = static_cast<ai_real>(strtol(comp, nullptr, 16)) / ai_real(255.0);
char g[3] = { buf[3], buf[4], '\0' };
diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
comp[0] = *buf;
++buf;
comp[1] = *buf;
++buf;
diffuse.b = static_cast<ai_real>(strtol(comp, nullptr, 16)) / ai_real(255.0);
char b[3] = { buf[5], buf[6], '\0' };
diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
if (7 == len)
return true;
comp[0] = *buf;
++buf;
comp[1] = *buf;
++buf;
diffuse.a = static_cast<ai_real>(strtol(comp, nullptr, 16)) / ai_real(255.0);
char a[3] = { buf[7], buf[8], '\0' };
diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
return true;
}
@ -364,32 +499,29 @@ private:
}
aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
aiMaterial *mat(nullptr);
const char *name(nullptr);
const std::string nodeName = node.name();
if (nodeName == D3MF::XmlTag::basematerials_base) {
name = node.attribute(D3MF::XmlTag::basematerials_name.c_str()).as_string();
std::string stdMatName;
aiString matName;
std::string strId(to_string(basematerialsId));
stdMatName += "id";
stdMatName += strId;
stdMatName += "_";
if (nullptr != name) {
stdMatName += std::string(name);
} else {
stdMatName += "basemat_";
stdMatName += to_string(mMaterialCount - basematerialsId);
}
matName.Set(stdMatName);
aiMaterial *material = new aiMaterial();
material->mNumProperties = 0;
std::string name;
bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
mat = new aiMaterial;
mat->AddProperty(&matName, AI_MATKEY_NAME);
assignDiffuseColor(node, mat);
std::string stdMaterialName;
std::string strId(to_string(basematerialsId));
stdMaterialName += "id";
stdMaterialName += strId;
stdMaterialName += "_";
if (hasName) {
stdMaterialName += std::string(name);
} else {
stdMaterialName += "basemat_";
stdMaterialName += to_string(mMaterialCount - basematerialsId);
}
return mat;
aiString assimpMaterialName(stdMaterialName);
material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
assignDiffuseColor(node, material);
return material;
}
private:
@ -399,8 +531,8 @@ private:
};
std::vector<MetaEntry> mMetaData;
std::vector<aiMesh *> mMeshes;
std::map<unsigned int, std::vector<std::pair<unsigned int, aiMaterial *>>> mBasematerialsDictionnary;
unsigned int mMaterialCount;
std::map<unsigned int, Resource*> mResourcesDictionnary;
unsigned int mMaterialCount, mMeshCount;
XmlParser *mXmlParser;
};