From dae9cd08d7245700bf9c95ec8089259cfe3b5878 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Tue, 1 Mar 2022 20:10:54 +0200 Subject: [PATCH] beginning of XML model reader --- .gitmodules | 3 + CMakeLists.txt | 10 ++- bone.h | 7 +- external/tinyxml2 | 1 + ikSolver.cpp | 4 +- ikSolverTest.cpp | 2 +- model.cpp | 5 +- node.cpp | 4 + node.h | 4 + script.py | 14 +++- skeleton.cpp | 6 +- vbModelReader.cpp | 2 +- xmlModelReader.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++++ xmlModelReader.h | 26 ++++++ 14 files changed, 273 insertions(+), 16 deletions(-) create mode 160000 external/tinyxml2 create mode 100644 xmlModelReader.cpp create mode 100644 xmlModelReader.h diff --git a/.gitmodules b/.gitmodules index 4ad01bd..d490684 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "external/assimp"] path = external/assimp url = https://github.com/assimp/assimp +[submodule "external/tinyxml2"] + path = external/tinyxml2 + url = https://github.com/leethomason/tinyxml2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 220b54b..4528392 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(MATH quaternion.cpp vector.cpp matrix.cpp ray.cpp) set(RENDER lineRenderer.cpp particleEmitter.cpp camera.cpp light.cpp material.cpp mesh.cpp model.cpp node.cpp quad.cpp box.cpp root.cpp shader.cpp texture.cpp) set(ANIM animationController.cpp animationChannel.cpp animation.cpp animatable.h keyframeChannel.cpp driver.cpp) set(ARMATURE skeleton.cpp bone.cpp ikSolver.cpp) -set(ASSET_READERS abstractAssetReader.cpp imageReader.cpp fontReader.cpp modelReader.cpp assimpModelReader.cpp vbModelReader.cpp) +set(ASSET_READERS abstractAssetReader.cpp imageReader.cpp fontReader.cpp modelReader.cpp assimpModelReader.cpp vbModelReader.cpp xmlModelReader.cpp) set(ASSET_MANAGER assetManager.cpp textAsset.h imageAsset.h modelAsset.h fontAsset.h) set(LIB_SRC ${RENDER} ${MATH} ${UTILS} ${GUI} ${ARMATURE} ${ASSET_MANAGER} ${ASSET_READERS} ${ANIM}) @@ -28,6 +28,7 @@ include_directories(external/stb) add_library(${LIB_NAME} SHARED external/glad/glad.c ${LIB_SRC}) +set(TINYXML2_DIR external/tinyxml2) set(GLFW_DIR external/glfw) set(FREETYPE_DIR external/freetype) set(ASSIMP_DIR external/assimp) @@ -39,6 +40,11 @@ elseif(WIN32) message("Building for Windows") endif() +add_subdirectory(${TINYXML2_DIR}) +set(TINYXML2_LIB_DIR build/${TINYXML2_DIR}) +target_include_directories(${LIB_NAME} PUBLIC ${TINYXML2_DIR}) +target_link_directories(${LIB_NAME} PUBLIC ${TINYXML2_LIB_DIR}) + add_subdirectory(${GLFW_DIR}) set(GLFW_LIB_DIR build/${GLFW_DIR}/src) target_include_directories(${LIB_NAME} PUBLIC ${GLFW_DIR}/include/GLFW) @@ -54,7 +60,7 @@ add_subdirectory(${FREETYPE_DIR}) target_include_directories(${LIB_NAME} PUBLIC ${FREETYPE_DIR}/include) target_link_directories(${LIB_NAME} PUBLIC ${FREETYPE_LIB_DIR}) -set(DEPS glfw assimp freetype ${CMAKE_DL_LIBS}) +set(DEPS glfw assimp freetype ${CMAKE_DL_LIBS} tinyxml2) target_link_libraries(${LIB_NAME} ${DEPS}) diff --git a/bone.h b/bone.h index cde0de6..9542e7e 100644 --- a/bone.h +++ b/bone.h @@ -17,8 +17,9 @@ namespace vb01{ void lookAt(Vector3, Vector3); Vector3 getBoneSpaceRestPos(Bone*); Vector3 getModelSpacePos(); - inline Bone* getIkTarget(){return ikTarget;} - inline void setIkTarget(Bone *target){this->ikTarget = target;} + inline Skeleton* getSkeleton(){return skeleton;} + inline std::string getIkTarget(){return ikTarget;} + inline void setIkTarget(std::string target){this->ikTarget = target;} inline int getIkChainLength(){return ikChainLength;} inline void setIkChainLength(int ikChainLength){this->ikChainLength = ikChainLength;} inline float getLength(){return length;} @@ -39,7 +40,7 @@ namespace vb01{ virtual float getDriverValue(Driver::VariableType); float length; - Bone *ikTarget = nullptr; + std::string ikTarget = ""; int ikChainLength = -1; bool ikFromTail = true; Skeleton *skeleton = nullptr; diff --git a/external/tinyxml2 b/external/tinyxml2 new file mode 160000 index 0000000..a977397 --- /dev/null +++ b/external/tinyxml2 @@ -0,0 +1 @@ +Subproject commit a9773976845b19e89020c1215781e71116477ef1 diff --git a/ikSolver.cpp b/ikSolver.cpp index e86abd7..041d041 100644 --- a/ikSolver.cpp +++ b/ikSolver.cpp @@ -1,4 +1,5 @@ #include "ikSolver.h" +#include "skeleton.h" #include "bone.h" namespace vb01{ @@ -7,7 +8,8 @@ namespace vb01{ for(int i = 0; i < chainLength; i++) sumLengths += boneChain[i]->getLength(); - Bone *subBase = (Bone*)boneChain[0]->getIkTarget()->getParent(); + Skeleton *skeleton = boneChain[0]->getSkeleton(); + Bone *subBase = (Bone*)skeleton->getBone(boneChain[0]->getIkTarget())->getParent(); Vector3 startPos = subBase->globalToLocalPosition(boneChain[chainLength - 1]->localToGlobalPosition(Vector3::VEC_ZERO)); if(startPos.getDistanceFrom(targetPos) < sumLengths){ int numIterations = 500; diff --git a/ikSolverTest.cpp b/ikSolverTest.cpp index 73c9aef..1ddd5a5 100644 --- a/ikSolverTest.cpp +++ b/ikSolverTest.cpp @@ -27,7 +27,7 @@ namespace vb01{ boneD->lookAt(Vector3::VEC_K, Vector3::VEC_J); boneD->setIkChainLength(chainLength); - boneD->setIkTarget(ikBone); + boneD->setIkTarget(ikBone->getName()); ikPos = new Vector3[chainLength]; boneChain = new Bone*[chainLength]; diff --git a/model.cpp b/model.cpp index 0af8186..f380288 100755 --- a/model.cpp +++ b/model.cpp @@ -8,6 +8,7 @@ #include "skeleton.h" #include "animation.h" #include "vbModelReader.h" +#include "xmlModelReader.h" #include "assimpModelReader.h" #include @@ -25,7 +26,9 @@ namespace vb01{ string extension = path.substr(dotId + 1, string::npos); ModelReader *modelReader = nullptr; - if(extension == "vb") + if(extension == "xml") + modelReader = new XmlModelReader(this, path); + else if(extension == "vb") modelReader = new VbModelReader(this, path); else modelReader = new AssimpModelReader(this, path); diff --git a/node.cpp b/node.cpp index 49a273c..7935bca 100755 --- a/node.cpp +++ b/node.cpp @@ -118,6 +118,10 @@ namespace vb01{ mesh->setNode(this); } + void Node::addSkeleton(Skeleton *skeleton){ + skeletons.push_back(skeleton); + } + void Node::attachParticleEmitter(ParticleEmitter *emitter){ emitters.push_back(emitter); emitter->setNode(this); diff --git a/node.h b/node.h index fc82d75..252ea04 100755 --- a/node.h +++ b/node.h @@ -16,11 +16,14 @@ namespace vb01{ class Light; class ParticleEmitter; class Text; + class Skeleton; + class AnimationController; class Node : public Animatable{ public: Node(Vector3 = Vector3::VEC_ZERO, Quaternion = Quaternion::QUAT_W, Vector3 = Vector3::VEC_IJK, std::string = ""); virtual ~Node(); + void addSkeleton(Skeleton*); void attachMesh(Mesh*); void attachParticleEmitter(ParticleEmitter*); virtual void update(); @@ -81,6 +84,7 @@ namespace vb01{ std::vector children; std::vector lights; std::vector texts; + std::vector skeletons; std::string name; Node *parent = nullptr; }; diff --git a/script.py b/script.py index f058afd..558c977 100644 --- a/script.py +++ b/script.py @@ -5,7 +5,6 @@ rootTag = ET.Element('model') def exportBone(armature, parentBone, parentTag, exportChildren = True): head = parentBone.head - tail = parentBone.tail xAxis = parentBone.x_axis yAxis = parentBone.y_axis ikTarget = None @@ -16,9 +15,13 @@ def exportBone(armature, parentBone, parentTag, exportChildren = True): boneTag.set('hx', str(head.x)) boneTag.set('hy', str(head.y)) boneTag.set('hz', str(head.z)) - boneTag.set('tx', str(tail.x)) - boneTag.set('ty', str(tail.y)) - boneTag.set('tz', str(tail.z)) + boneTag.set('length', str(parentBone.length)) + boneTag.set('xaxisx', str(xAxis.x)) + boneTag.set('xaxisy', str(xAxis.y)) + boneTag.set('xaxisz', str(xAxis.z)) + boneTag.set('yaxisx', str(yAxis.x)) + boneTag.set('yaxisy', str(yAxis.y)) + boneTag.set('yaxisz', str(yAxis.z)) ikConstraint = None @@ -153,6 +156,9 @@ def export(node, parentTag): numShapeKeys = len(mesh.shape_keys.key_blocks) - 1 meshTag = ET.SubElement(nodeTag, 'mesh') + meshTag.set('num_faces', str(numFaces)) + meshTag.set('num_vertex_groups', str(numGroups)) + meshTag.set('num_shape_keys', str(numShapeKeys)) for vert in mesh.vertices: pos = vert.co diff --git a/skeleton.cpp b/skeleton.cpp index 617a338..74568ad 100644 --- a/skeleton.cpp +++ b/skeleton.cpp @@ -21,7 +21,7 @@ namespace vb01{ void Skeleton::updateIk(){ for(Bone *b : bones){ - if(b->getIkTarget()){ + if(b->getIkTarget() != ""){ const int chainLength = b->getIkChainLength(); Bone **boneChain = getIkBoneChain(b); for(int i = 0; i < chainLength; i++){ @@ -33,13 +33,13 @@ namespace vb01{ } } for(Bone *b : bones) - if(b->getIkTarget()) + if(getBone(b->getIkTarget())) solveIk(b); } void Skeleton::solveIk(Bone *ikBone){ const int chainLength = ikBone->getIkChainLength(); - Bone *ikTarget = ikBone->getIkTarget(); + Bone *ikTarget = getBone(ikBone->getIkTarget()); Bone *subBase = (Bone*)ikTarget->getParent(); Bone **boneChain = getIkBoneChain(ikBone); diff --git a/vbModelReader.cpp b/vbModelReader.cpp index 06fd1a0..7a28804 100644 --- a/vbModelReader.cpp +++ b/vbModelReader.cpp @@ -225,7 +225,7 @@ namespace vb01{ for(it = ikRelationships.begin(); it != ikRelationships.end(); it++){ string targetBoneName = it->first; string ikTargetBoneName = ikRelationships[targetBoneName]; - skeleton->getBone(targetBoneName)->setIkTarget(skeleton->getBone(ikTargetBoneName)); + skeleton->getBone(targetBoneName)->setIkTarget(ikTargetBoneName); } } diff --git a/xmlModelReader.cpp b/xmlModelReader.cpp new file mode 100644 index 0000000..2356c88 --- /dev/null +++ b/xmlModelReader.cpp @@ -0,0 +1,201 @@ +#include "xmlModelReader.h" +#include "mesh.h" +#include "skeleton.h" +#include "bone.h" +#include "vector.h" + +#include + +#include + +using namespace std; +using namespace tinyxml2; + +namespace vb01{ + XmlModelReader::XmlModelReader(Model *model, string path) : ModelReader(model, path){ + XMLDocument *doc = new XMLDocument(); + doc->LoadFile(path.c_str()); + + XMLNode *root = doc->FirstChild(); + model->attachChild(processNode(model, (XMLElement*)root)); + } + + Mesh* XmlModelReader::processMesh(XMLElement *meshEl){ + vector vertPos, vertNorm; + vector weights; + const char *tagName = "vertdata"; + + for(XMLElement *vertEl = meshEl->FirstChildElement(tagName); vertEl && strcmp(vertEl->Name(), tagName) == 0; vertEl = vertEl->NextSiblingElement()){ + float posX = atof(vertEl->Attribute("px")); + float posY = atof(vertEl->Attribute("py")); + float posZ = atof(vertEl->Attribute("pz")); + vertPos.push_back(Vector3(posX, posY, posZ)); + + float normX = atof(vertEl->Attribute("nx")); + float normY = atof(vertEl->Attribute("ny")); + float normZ = atof(vertEl->Attribute("nz")); + vertNorm.push_back(Vector3(normX, normY, normZ)); + + for(XMLElement *weightEl = vertEl->FirstChildElement("weight"); weightEl; weightEl = weightEl->NextSiblingElement()) + weights.push_back(atof(weightEl->Attribute("value"))); + } + + int numVertexGroups = atoi(meshEl->Attribute("num_vertex_groups")); + string *vertexGroups = new string[numVertexGroups]; + tagName = "vertexgroup"; + int i = 0; + + for(XMLElement *vertGroupEl = meshEl->FirstChildElement(tagName); vertGroupEl && strcmp(vertGroupEl->Name(), tagName) == 0; vertGroupEl = vertGroupEl->NextSiblingElement(), i++) + vertexGroups[i] = (vertGroupEl->Attribute("name")); + + i = 0; + int numVertices = 3 * atoi(meshEl->Attribute("num_faces")); + int numBones; + Mesh::Vertex *vertices = new Mesh::Vertex[numVertices]; + tagName = "vert"; + vector vertIds; + u32 *indices = new u32[numVertices]; + + for(XMLElement *vertEl = meshEl->FirstChildElement(tagName); vertEl && strcmp(vertEl->Name(), tagName) == 0; vertEl = vertEl->NextSiblingElement(), i++){ + int id = atoi(vertEl->Attribute("id")); + vertIds.push_back(id); + + float uvX = atof(vertEl->Attribute("uvx")); + float uvY = atof(vertEl->Attribute("uvy")); + Vector2 uv = Vector2(uvX, uvY); + + float tanX = atof(vertEl->Attribute("tx")); + float tanY = atof(vertEl->Attribute("ty")); + float tanZ = atof(vertEl->Attribute("tz")); + Vector3 tan = Vector3(tanX, tanY, tanZ); + + float biTanX = atof(vertEl->Attribute("bx")); + float biTanY = atof(vertEl->Attribute("by")); + float biTanZ = atof(vertEl->Attribute("bz")); + Vector3 biTan = Vector3(biTanX, biTanY, biTanZ); + + Mesh::Vertex vertex; + vertex.pos = vertPos[id]; + vertex.norm = vertNorm[id]; + vertex.uv = uv; + vertex.tan = tan; + vertex.biTan = biTan; + + for(int j = 0, boneIndex = 0; j < numBones; j++){ + if(weights[id * numBones + j] > 0){ + vertex.boneIndices[boneIndex] = j; + vertex.weights[boneIndex] = weights[id * numBones + j]; + boneIndex++; + } + } + + indices[i] = i; + vertices[i] = vertex; + } + + vertNorm.clear(); + tagName = "shapekey"; + i = 0; + int numShapeKeys = atoi(meshEl->Attribute("num_shape_keys")); + Mesh::ShapeKey *shapeKeys = new Mesh::ShapeKey[numShapeKeys]; + + for(XMLElement *shapeKeyEl = meshEl->FirstChildElement(tagName); shapeKeyEl && strcmp(shapeKeyEl->Name(), tagName) == 0; shapeKeyEl = shapeKeyEl->NextSiblingElement(), i++){ + shapeKeys[i].name = shapeKeyEl->Attribute("name"); + shapeKeys[i].minValue = atof(shapeKeyEl->Attribute("min")); + shapeKeys[i].maxValue = atof(shapeKeyEl->Attribute("max")); + + XMLElement *driverEl = shapeKeyEl->FirstChildElement("driver"); + + vector shapeKeyPos; + + for(XMLElement *vertEl = shapeKeyEl->FirstChildElement(tagName); vertEl; vertEl = vertEl->NextSiblingElement()){ + float posX = atof(vertEl->Attribute("px")); + float posY = atof(vertEl->Attribute("py")); + float posZ = atof(vertEl->Attribute("pz")); + shapeKeyPos.push_back(Vector3(posX, posY, posZ)); + + for(int j = 0; j < numVertices; j++) + vertices[j].shapeKeyOffsets[i] = shapeKeyPos[vertIds[j]] - vertPos[vertIds[j]]; + } + } + + Mesh *mesh = new Mesh(vertices, indices, numVertices / 3, vertexGroups, numBones, shapeKeys, numShapeKeys); + return mesh; + } + + Skeleton* XmlModelReader::processSkeleton(Node *root, XMLElement *skeletonEl){ + XMLElement *rootBoneEl = skeletonEl->FirstChildElement("bone"); + Skeleton *skeleton = new Skeleton(nullptr); + skeleton->addBone((Bone*)processNode(root, rootBoneEl, true), (Bone*)root); + + return skeleton; + } + + Node* XmlModelReader::processNode(Node *parent, XMLElement *xmlEl, bool bone){ + Node *node = nullptr; + + if(bone){ + float headX = atof(xmlEl->Attribute("hx")); + float headY = atof(xmlEl->Attribute("hy")); + float headZ = atof(xmlEl->Attribute("hz")); + Vector3 head = Vector3(headX, headY, headZ); + + float xAxisX = atof(xmlEl->Attribute("xaxisx")); + float xAxisY = atof(xmlEl->Attribute("xaxisy")); + float xAxisZ = atof(xmlEl->Attribute("xaxisz")); + Vector3 xAxis = Vector3(xAxisX, xAxisY, xAxisZ); + + float yAxisX = atof(xmlEl->Attribute("yaxisx")); + float yAxisY = atof(xmlEl->Attribute("yaxisy")); + float yAxisZ = atof(xmlEl->Attribute("yaxisz")); + Vector3 yAxis = Vector3(yAxisX, yAxisY, yAxisZ); + + string name = xmlEl->Attribute("name"); + float length = atof(xmlEl->Attribute("length")); + + Vector3 zAxis = xAxis.cross(yAxis).norm(); + Vector3 pos = head; + + /* + if(!parent){ + parent = model; + swap(pos.y, pos.z); + pos.z = -pos.z; + swap(yAxis.y, yAxis.z); + yAxis.z = -yAxis.z; + swap(zAxis.y, zAxis.z); + zAxis.z = -zAxis.z; + } + else + */ + pos = pos + Vector3(0, ((Bone*)parent)->getLength(), 0); + + node = new Bone(name, length, pos, Quaternion::QUAT_W, Vector3::VEC_IJK); + node->lookAt(zAxis, yAxis); + const char *ikTarget = xmlEl->Attribute("iktarget"); + + if(ikTarget != nullptr){ + ((Bone*)node)->setIkTarget(ikTarget); + int ikChainLength = atoi(xmlEl->Attribute("chainlength")); + ((Bone*)node)->setIkChainLength(ikChainLength); + } + } + + XMLElement *skeletonTag = xmlEl->FirstChildElement("skeleton"); + + if(skeletonTag) + processSkeleton(node, skeletonTag); + + XMLElement *meshTag = xmlEl->FirstChildElement("mesh"); + + if(meshTag) + processMesh(meshTag); + + XMLElement *lightTag = xmlEl->FirstChildElement("light"); + + for(XMLElement *el = xmlEl->FirstChildElement("node"); el; el = el->NextSiblingElement()) + parent->attachChild(processNode(parent, el, bone)); + + return node; + } +} diff --git a/xmlModelReader.h b/xmlModelReader.h new file mode 100644 index 0000000..a556515 --- /dev/null +++ b/xmlModelReader.h @@ -0,0 +1,26 @@ +#ifndef XML_MODEL_READER_H +#define XML_MODEL_READER_H + +#include "modelReader.h" + +namespace tinyxml2{ + class XMLElement; +} + +namespace vb01{ + class Node; + class Mesh; + class Skeleton; + class Bone; + + class XmlModelReader : public ModelReader{ + public: + XmlModelReader(Model*, std::string); + private: + Node* processNode(Node*, tinyxml2::XMLElement*, bool = false); + Mesh* processMesh(tinyxml2::XMLElement*); + Skeleton* processSkeleton(Node*, tinyxml2::XMLElement*); + }; +} + +#endif