From 8ffc4e400dab181b5b5730e551e9ef1331ad4298 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 22 Nov 2016 20:14:42 +0900 Subject: [PATCH] Refactor scene graph(WIP). --- examples/alembic_to_gltf/abc2gltf.cc | 430 +++++++++++++++++++++------ 1 file changed, 341 insertions(+), 89 deletions(-) diff --git a/examples/alembic_to_gltf/abc2gltf.cc b/examples/alembic_to_gltf/abc2gltf.cc index 7bdf5bb..a3db315 100644 --- a/examples/alembic_to_gltf/abc2gltf.cc +++ b/examples/alembic_to_gltf/abc2gltf.cc @@ -44,6 +44,35 @@ class Node { public: Node() { + } + virtual ~Node(); + + Node(const Node& rhs) { + name = rhs.name; + children = rhs.children; + } + + Node &operator=(const Node& rhs) { + name = rhs.name; + children = rhs.children; + + return (*this); + } + + std::string name; + + std::vector children; +}; + +Node::~Node() +{ + +} + +class Xform : public Node +{ + public: + Xform() : Node() { xform[0] = 1.0; xform[1] = 0.0; xform[2] = 0.0; @@ -64,35 +93,28 @@ class Node xform[14] = 0.0; xform[15] = 1.0; } - virtual ~Node(); - Node(const Node& rhs) { - name = rhs.name; - for (size_t i = 0; i < 16; i++) { - xform[i] = rhs.xform[i]; - } + virtual ~Xform(); - children = rhs.children; - } + Xform &operator=(const Xform &rhs) { - Node &operator=(const Node& rhs) { - name = rhs.name; - for (size_t i = 0; i < 16; i++) { - xform[i] = rhs.xform[i]; - } - - children = rhs.children; + if (this != &rhs) { + Node::operator=(rhs); + + for (size_t i = 0; i < 16; i++) { + xform[i] = rhs.xform[i]; + } + } return (*this); } + + - std::string name; double xform[16]; - - std::vector children; }; -Node::~Node() +Xform::~Xform() { } @@ -100,16 +122,20 @@ Node::~Node() class Mesh : public Node { public: - Mesh() {} + Mesh() : Node() {} virtual ~Mesh(); Mesh &operator=(const Mesh& rhs) { - vertices = rhs.vertices; - normals = rhs.normals; - facevarying_normals = rhs.facevarying_normals; - texcoords = rhs.texcoords; - facevarying_texcoords = rhs.facevarying_texcoords; - faces = rhs.faces; + if (this != &rhs) { + Node::operator=(rhs); + + vertices = rhs.vertices; + normals = rhs.normals; + facevarying_normals = rhs.facevarying_normals; + texcoords = rhs.texcoords; + facevarying_texcoords = rhs.facevarying_texcoords; + faces = rhs.faces; + } return (*this); } @@ -139,12 +165,16 @@ Mesh::~Mesh() class Curves : public Node { public: - Curves() {} + Curves() : Node() {} virtual ~Curves(); Curves &operator=(const Curves& rhs) { - points = rhs.points; - nverts = rhs.nverts; + if (this != &rhs) { + Node::operator=(rhs); + + points = rhs.points; + nverts = rhs.nverts; + } return (*this); } @@ -162,12 +192,16 @@ Curves::~Curves() class Points : public Node { public: - Points() {} + Points() : Node() {} ~Points(); Points &operator=(const Points& rhs) { - points = rhs.points; - radiuss = rhs.radiuss; + if (this != &rhs) { + Node::operator=(rhs); + + points = rhs.points; + radiuss = rhs.radiuss; + } return (*this); } @@ -183,15 +217,43 @@ Points::~Points() // TODO(Nurbs) -typedef struct +class Scene { - std::map mesh_map; - std::map curves_map; + public: + Scene() : root_node(NULL) {} + ~Scene() { + Destroy(); + } + + void Destroy() { + delete root_node; + root_node = NULL; + + { + std::map::const_iterator it(mesh_map.begin()); + for (; it != mesh_map.end(); it++) { + delete it->second; + } + } + + { + std::map::const_iterator it(curves_map.begin()); + for (; it != curves_map.end(); it++) { + delete it->second; + } + } + + } + + + std::map mesh_map; + std::map curves_map; //std::map points_map; - std::map node_map; + const Node* root_node; + //std::map node_map; -} Scene; +}; // ---------------------------------------------------------------- // writer module @@ -515,10 +577,11 @@ static void readPolyUVs( } -// Traverse Alembic object tree and extract scene data. -static void VisitObjectAndExtractScene(Node* node_out, std::stringstream& ss, const Alembic::AbcGeom::IObject& obj, const std::string& indent) { +// Traverse Alembic object tree and extract data. +static void VisitObjectAndExtractNode(Node* node_out, std::stringstream& ss, const Alembic::AbcGeom::IObject& obj, const std::string& indent) { std::string path = obj.getFullName(); + node_out->name = path; if (path.compare("/") != 0) { ss << "Object: path = " << path << std::endl; @@ -548,7 +611,7 @@ static void VisitObjectAndExtractScene(Node* node_out, std::stringstream& ss, co Alembic::AbcGeom::M44d static_matrix = xform.getSchema().getValue().getMatrix(); ss << "IXform static: " << header.getName() << ", " << static_matrix << std::endl; - Node *xform_node = new Node(); + Xform *xform_node = new Xform(); xform_node->name = header.getName(); xform_node->xform[0] = static_matrix[0][0]; xform_node->xform[1] = static_matrix[0][1]; @@ -634,10 +697,6 @@ static void VisitObjectAndExtractScene(Node* node_out, std::stringstream& ss, co node = mesh; - //assert(scene->mesh_map.find(path) == scene->mesh_map.end()); - //scene->mesh_map[path] = mesh; - //return; - } else { std::cout << "Warning: # of samples = 0" << std::endl; } @@ -701,10 +760,6 @@ static void VisitObjectAndExtractScene(Node* node_out, std::stringstream& ss, co memcpy(curves.nverts.data(), num_vertices->get(), sizeof(int) * num_vertices->size()); } - //assert(scene->curves_map.find(path) == scene->curves_map.end()); - //scene->curves_map[path] = curves; - //return; - } else { std::cout << "Warning: # of samples = 0" << std::endl; } @@ -717,60 +772,73 @@ static void VisitObjectAndExtractScene(Node* node_out, std::stringstream& ss, co if (node) { // Visit child. - VisitObjectAndExtractScene(node, ss, Alembic::AbcGeom::IObject(obj, obj.getChildHeader(i).getName()), indent); + VisitObjectAndExtractNode(node, ss, Alembic::AbcGeom::IObject(obj, obj.getChildHeader(i).getName()), indent); } node_out->children.push_back(node); } } -static bool SaveSceneToGLTF(picojson::object *out, const Scene &scene) +static bool ConvertNodeToGLTF(picojson::object *out, const Node *node) { - { - std::map::const_iterator it(scene.node_map.begin()); - std::map::const_iterator it_end(scene.node_map.end()); - picojson::object nodes; + assert(!node->name.empty()); - for (; it != it_end; it++) { - picojson::object node; + // FIXME(syoyo): Add serialization method for each class. + if (dynamic_cast(node)) { + const Xform *xform = dynamic_cast(node); - // FIXME: Hierarchial - picojson::array meshes; + std::cout << "root xform" << node->name << std::endl; - meshes.push_back(picojson::value(std::string("mesh_1"))); - - node["meshes"] = picojson::value(meshes); - - // Xform - // TODO(syoyo): Support Translation, Scale, Rotate, etc. - { - picojson::array xform; - for (size_t i = 0; i < 16; i++) { - xform.push_back(picojson::value((it->second)->xform[i])); - } - node["matrix"] = picojson::value(xform); - } - - nodes["node_1"] = picojson::value(node); - (*out)["nodes"] = picojson::value(nodes); + picojson::object json_node; + picojson::array json_xform; + for (size_t i = 0; i < 16; i++) { + json_xform.push_back(picojson::value(xform->xform[i])); } - } + json_node["matrix"] = picojson::value(json_xform); + json_node["name"] = picojson::value(xform->name); + //json_xform["meshes"] = // TODO - { - picojson::object defaultScene; - picojson::array nodes; + picojson::array json_children; + if (xform->children.size() > 0) { + for (size_t i = 0; i < xform->children.size(); i++) { + picojson::object json_child_node; + ConvertNodeToGLTF(&json_child_node, xform->children[i]); + } - nodes.push_back(picojson::value(std::string("node_1"))); + json_node["children"] = picojson::value(json_children); + } - defaultScene["nodes"] = picojson::value(nodes); - - (*out)["scene"] = picojson::value("defaultScene"); - picojson::object scenes; - scenes["defaultScene"] = picojson::value(defaultScene); - (*out)["scenes"] = picojson::value(scenes); + (*out)[node->name] = picojson::value(json_node); + } else { + return false; } + return true; +} + +static bool ConvertSceneToGLTF(picojson::object *out, const Scene &scene) +{ + picojson::object defaultScene; + picojson::array nodes; + + if (scene.root_node) { + + picojson::object node; + + ConvertNodeToGLTF(&node, scene.root_node); + + nodes.push_back(picojson::value(node)); + + } + + defaultScene["nodes"] = picojson::value(nodes); + + (*out)["scene"] = picojson::value("defaultScene"); + picojson::object scenes; + scenes["defaultScene"] = picojson::value(defaultScene); + (*out)["scenes"] = picojson::value(scenes); + // @todo {} picojson::object shaders; @@ -787,6 +855,7 @@ static bool SaveSceneToGLTF(picojson::object *out, const Scene &scene) return true; } +#if 0 static bool SaveMeshToGLTF(const std::string& output_filename, const Mesh& mesh) { picojson::object root; @@ -1132,6 +1201,155 @@ static bool SaveCurvesToGLTF(const std::string& output_filename, return true; } +#endif + +static bool SaveSceneToGLTF(const std::string& output_filename, + const Scene& scene) { + picojson::object root; + + ConvertSceneToGLTF(&root, scene); + + // Write header. + { + picojson::object asset; + asset["generator"] = picojson::value("abc2gltf"); + asset["premultipliedAlpha"] = picojson::value(true); + asset["version"] = picojson::value(static_cast(1)); + picojson::object profile; + profile["api"] = picojson::value("WebGL"); + profile["version"] = picojson::value("1.0.2"); + asset["profile"] = picojson::value(profile); + root["assets"] = picojson::value(asset); + } + +#if 0 + { + picojson::object buffers; + { + std::string vertices_b64data = base64_encode(reinterpret_cast(mesh.vertices.data()), mesh.vertices.size() * sizeof(float)); + picojson::object buf; + + buf["type"] = picojson::value("arraybuffer"); + buf["uri"] = picojson::value( + std::string("data:application/octet-stream;base64,") + vertices_b64data); + buf["byteLength"] = + picojson::value(static_cast(mesh.vertices.size() * sizeof(float))); + + buffers["vertices"] = picojson::value(buf); + } + { + std::string faces_b64data = base64_encode(reinterpret_cast(mesh.faces.data()), mesh.faces.size() * sizeof(unsigned int)); + picojson::object buf; + + buf["type"] = picojson::value("arraybuffer"); + buf["uri"] = picojson::value( + std::string("data:application/octet-stream;base64,") + faces_b64data); + buf["byteLength"] = + picojson::value(static_cast(mesh.faces.size() * sizeof(unsigned int))); + + buffers["indices"] = picojson::value(buf); + } + root["buffers"] = picojson::value(buffers); + } + + { + picojson::object buffer_views; + { + picojson::object buffer_view_vertices; + buffer_view_vertices["buffer"] = picojson::value(std::string("vertices")); + buffer_view_vertices["byteLength"] = picojson::value(static_cast(mesh.vertices.size() * sizeof(float))); + buffer_view_vertices["byteOffset"] = picojson::value(static_cast(0)); + buffer_view_vertices["target"] = picojson::value(static_cast(TINYGLTF_TARGET_ARRAY_BUFFER)); + + buffer_views["bufferView_vertices"] = picojson::value(buffer_view_vertices); + } + + { + picojson::object buffer_view_indices; + buffer_view_indices["buffer"] = picojson::value(std::string("indices")); + buffer_view_indices["byteLength"] = picojson::value(static_cast(mesh.faces.size() * sizeof(unsigned int))); + buffer_view_indices["byteOffset"] = picojson::value(static_cast(0)); + buffer_view_indices["target"] = picojson::value(static_cast(TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)); + + buffer_views["bufferView_indices"] = picojson::value(buffer_view_indices); + } + + root["bufferViews"] = picojson::value(buffer_views); + } + + { + picojson::object attributes; + + attributes["POSITION"] = picojson::value(std::string("accessor_vertices")); + + + picojson::object primitive; + primitive["attributes"] = picojson::value(attributes); + primitive["indices"] = picojson::value("accessor_indices"); + primitive["material"] = picojson::value("material_1"); + primitive["mode"] = picojson::value(static_cast(TINYGLTF_MODE_TRIANGLES)); + + picojson::array primitive_array; + primitive_array.push_back(picojson::value(primitive)); + + picojson::object m; + m["primitives"] = picojson::value(primitive_array); + + picojson::object meshes; + meshes["mesh_1"] = picojson::value(m); + + + root["meshes"] = picojson::value(meshes); + } + + { + picojson::object accessors; + picojson::object accessor_vertices; + picojson::object accessor_indices; + + accessor_vertices["bufferView"] = picojson::value(std::string("bufferView_vertices")); + accessor_vertices["byteOffset"] = picojson::value(static_cast(0)); + accessor_vertices["byteStride"] = picojson::value(static_cast(3 * sizeof(float))); + accessor_vertices["componentType"] = picojson::value(static_cast(TINYGLTF_COMPONENT_TYPE_FLOAT)); + accessor_vertices["count"] = picojson::value(static_cast(mesh.vertices.size())); + accessor_vertices["type"] = picojson::value(std::string("VEC3")); + accessors["accessor_vertices"] = picojson::value(accessor_vertices); + + accessor_indices["bufferView"] = picojson::value(std::string("bufferView_indices")); + accessor_indices["byteOffset"] = picojson::value(static_cast(0)); + accessor_indices["componentType"] = picojson::value(static_cast(TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)); + accessor_indices["count"] = picojson::value(static_cast(mesh.faces.size())); + accessor_indices["type"] = picojson::value(std::string("SCALAR")); + accessors["accessor_indices"] = picojson::value(accessor_indices); + root["accessors"] = picojson::value(accessors); + } + + { + // Use Default Material(Do not supply `material.technique`) + picojson::object default_material; + picojson::object materials; + + materials["material_1"] = picojson::value(default_material); + + root["materials"] = picojson::value(materials); + + } +#endif + + std::ofstream ifs(output_filename.c_str()); + if (ifs.bad()) { + std::cerr << "Failed to open " << output_filename << std::endl; + return false; + } + + picojson::value v = picojson::value(root); + + std::string s = v.serialize(/* pretty */true); + ifs.write(s.data(), static_cast(s.size())); + ifs.close(); + + return true; +} #if 0 // Flatten multiple Curves object to one Curves. @@ -1150,6 +1368,35 @@ static void FlattenCurves(Curves *flatten_curves, const Scene &scene) } #endif +// Flatten +void ConvertNodeToScene(Scene *scene, const Node *node) +{ + if (!node) return; + + std::cout << "node " << node->name << std::endl; + + if (dynamic_cast(node)) { + // Allow only one root node in the scene. + if (scene->root_node == NULL) { + scene->root_node = node; + } else { + // Assume child xform node + } + + } else if (dynamic_cast(node)) { + assert(scene->mesh_map.find(node->name) == scene->mesh_map.end()); + scene->mesh_map[node->name] = dynamic_cast(node); + } else if (dynamic_cast(node)) { + assert(scene->curves_map.find(node->name) == scene->curves_map.end()); + scene->curves_map[node->name] = dynamic_cast(node); + } + + for (size_t i = 0; i < node->children.size(); i++) { + ConvertNodeToScene(scene, node->children[i]); + } + +} + int main(int argc, char** argv) { std::string abc_filename; std::string gltf_filename; @@ -1171,14 +1418,19 @@ int main(int argc, char** argv) { Scene scene; std::stringstream ss; - Node node; - VisitObjectAndExtractScene(&node, ss, root, /* indent */" "); + Node* node = new Node(); + VisitObjectAndExtractNode(node, ss, root, /* indent */" "); - std::cout << ss.str() << std::endl; + //std::cout << ss.str() << std::endl; + ConvertNodeToScene(&scene, node); + delete node; + + SaveSceneToGLTF(gltf_filename, scene); + +#if 0 if (scene.curves_map.size() > 0) { } -#if 0 if (foundMesh) { bool ret = SaveMeshToGLTF(gltf_filename, mesh); if (ret) {