{"id":18420663,"url":"https://github.com/openworm/skeletonextraction","last_synced_at":"2025-07-03T07:06:09.296Z","repository":{"id":18649377,"uuid":"21856265","full_name":"openworm/skeletonExtraction","owner":"openworm","description":"Transforms Sibernetic output into a COLLADA animation","archived":false,"fork":false,"pushed_at":"2016-12-19T17:23:00.000Z","size":111551,"stargazers_count":7,"open_issues_count":8,"forks_count":2,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-04-07T13:38:01.749Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openworm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-15T10:54:12.000Z","updated_at":"2023-07-04T06:42:23.000Z","dependencies_parsed_at":"2022-09-07T02:02:04.042Z","dependency_job_id":null,"html_url":"https://github.com/openworm/skeletonExtraction","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/openworm/skeletonExtraction","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openworm%2FskeletonExtraction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openworm%2FskeletonExtraction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openworm%2FskeletonExtraction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openworm%2FskeletonExtraction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openworm","download_url":"https://codeload.github.com/openworm/skeletonExtraction/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openworm%2FskeletonExtraction/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263279273,"owners_count":23441682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T04:22:37.372Z","updated_at":"2025-07-03T07:06:04.281Z","avatar_url":"https://github.com/openworm.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"1/ Data from the simulation are loaded, for this there are 2 objects - cuticleLoader and MuscleLoader … for cuticle the cuticleLoader, position file and membrane file has to be set. Position file is taken from OW_DATA_VERSION data dir and membrane file is taken from OW_BINDPOSE_DATA_VERSION data dir.\n\ng_core-\u003eopenwormWrapper.cuticleLoader.ReadWormIteration(g_core-\u003eopenwormWrapper.mesh, timestep);\nImport::CalculatePerVertexNormals(g_core-\u003eopenwormWrapper.mesh);\n\n2/ All the cuticles in all the timesteps are loaded in a loop and exported into collada files (in order to be loaded by assimp, so SDF can be calculated). \n\ng_core-\u003eopenwormWrapper.exporter.Export( g_core-\u003eopenwormWrapper.mesh, config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\"+OW_DATA_VERSION+\"\\\\collada\\\\wormCollada_\" + g_core-\u003eexportWrapper.PadNumber( g_core-\u003eopenwormWrapper.timeStep, OW_PADDING) + \".dae\");\n\n3/ Next, wrapper is called from the script. It is loaded from the script in a loop, because there is a memory leak in SDF library. In can be run in 8 threads using 8 powershell scripts. By running this wrapper, skeletons and transformations between skeletons and a bindpose are calculated.\nThe output are quaternions, matrices and axis angles. They are rotations only encoding in the matrices.\n\ng_core-\u003eopenwormWrapper.skeletonExtractionSDF_calculateSDF(\u0026(g_engine-\u003eoModel), \u0026(g_core-\u003elbseWrapper.oLBSExtractor));\ng_core-\u003eopenwormWrapper.skeletonExtractionSDF_extractSkeleton(g_core-\u003edefaultSkeletonRoot, \u0026(g_core-\u003elbseWrapper.oLBSExtractor), \u0026(g_core-\u003esdfWrapper.oSDFExtractor));\t\t\t \n\n4) Postprocessing of the rotations. There are three steps in the postprocessing process.\n\na - interpolation is applied, SLERP is used for this\n(this is performed to get rid of the shivering of the worm caused by floating inaccuracy)\nSM::SmoothQuaternions(source, 130);  // 100-150 are good smoothing neighbourhoods\n\nb - SDF is calculated, muscle stretching and gravitation force is applied and composed into the matrices\n(muscle are stretching the cuticle, therefore the volume has to be restored)\n\nc - rotations are perform in the skeleton chain, the translation of the skeleton root is ignored\n(in each node, there is transformation for local node only and by multiplying of transformation matrices in skeleton chain we get global transformation matrix for each node, that can be directly used for skinning in shaders)\ng_core-\u003eopenwormWrapper.performSDFBasedTransformationsInSkeletonTree(\u0026g_core-\u003eopenwormWrapper.bindPoseSkeletonNode, \u0026source[i], \u0026matrices, sdf_loaded, 9.81, glm::vec3(0, 0, 1)); // b and c are both called in this method\n\nd - the skeleton is anchored in some skeleton node\n(for each timestep, the translation vector for anchored skeleton node is calculated - translation between bindpose position and the skinned one. Then this translation vector is added to each transformation matrix, do the skinning is applied and final skinned vertex is translated at the end by the vector)\ng_core-\u003eopenwormWrapper.anchorSkeletonDuringSkinning(\u0026g_core-\u003eopenwormWrapper.bindPoseSkeletonNode, \u0026matrices, 15); // last parameter is the index of the node\n\n###PROJECTS:###\n*All projects are in this repository's [Visual Studio Solution](http://msdn.microsoft.com/en-us/library/bb165951(v=vs.80).aspx) [file](https://github.com/openworm/skeletonExtraction/blob/master/OpenWormSkeletonExtraction.sln).*\n\n**1) GDSW_lib** [Geodesic Distance Skinning Weights]\n- Library computes geodesic distance on the mesh surface using Floyd-Warshall algorithm. \n- The geodesic distance is than used for computation of skinning weights\n- It is a very trivial approach how skinning weights can be computed\n- A more complex approach might be needed in the future\n\n*Main functions:*\n```\nvoid FloydWarshall(MeshGraph * pMesh, Array2D\u003cfloat\u003e\u0026 M)\n```\n- Computes distances on the graph\n\n**2) LBSE_lib** [Laplacian-Based Skeleton Extraction]\n- Library uses Laplacian based skeleton contraction for extraction of the skeleton from input mesh\n- The resulting skeleton is a tree structure\n\n*Main functions:*\n```\nvoid computeSkeleton(t3DModel *pModel, \n                     int sourcePointID, \n                     SN::SkeletonNode * skeleton, \n                     int * ite, \n                     bool \u0026recreateOperator, \n                     float modelMaxDim)\n```\n- Computes skeleton from input mesh\n\n**3) PCT_lib** [Point Cloud Triangulation]\n- Library computes triangulation from input point cloud\n- The global triangulation is composed from local Delaunay triangulations\n\n*Main functions:*\n```\nvoid computeLocalTriangulationFromPoints(int index, int numOfPoints, \n                                         float * points, int \u0026numOfIndices, \n                                         int ** indices, \n                                         std::vector\u003cstd::set\u003cint\u003e\u003e globalNeighbourhoods, \n                                         float * nor, bool visualization = false)\n```\n- Computes local triangulation\n```\nvoid computeGlobalTriangulationFromPoints(int numOfPoints, \n                                          float * points, \n                                          int \u0026numOfIndices, \n                                          int ** indices, \n                                          float ** normals, \n                                          bool visualization = false)\n```\n- computes global triangulation\n\n**4) SDF_lib** [Shape Diameter Function]\n- Not yet needed, but I have left it there for keeping compatibility of source files between my repo and this repo.\n\n###PREPROCESSOR DEFINITIONS:###\n```\nD_SCL_SECURE_NO_WARNINGS;_NLOG;_NMMGR;NOMINMAX;\n```\n###HOW DOES IT WORK:###\n\nWhen a process wants to extract a skeleton from an input mesh, the process has to call the `\"computeSkeleton\"` method from `LBSE_lib`. Next, for extraction of skinning weights, the `GDSW_lib` has to be used.\n\n###LIBRARIES NEEDED TO RUN EXTRACTION:###\n\n- Boost - used for serialization of the skeletons, can be removed later if serialization is not needed\n- TNT - vectors and matrices used in Jama\n- Jama - linear solver for TNT vectors and matrices\n- ViennaCL - linear solver with OpenCL support...the solver can be parallelized on the GPU\n- Fade2D - used for Delaunay triangulation\n\n\n###HOW TO POSTPROCESS QUATERNIONS, SMOOTH THEM AND ANCHOR THEM:###\n```\n int timeStep = OW_START_WORM_ITERATION, timeIncrease = 1;\n //load skeleton\n string file = config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\" + OW_DATA_VERSION + \"\\\\animation_\" + std::to_string(OW_SKINNING_NUM_BONES) + \"\\\\skeleton_\" + std::to_string(OW_SKINNING_NUM_BONES) + \"S_\"+ g_core-\u003eexportWrapper.PadNumber(OW_BINDPOSE_TIMESTEP, 4) + \".skl\";\n ifstream ifs(file);\n assert(ifs.good());\n boost::archive::xml_iarchive ia(ifs);\n ia \u003e\u003e BOOST_SERIALIZATION_NVP(g_core-\u003eopenwormWrapper.bindPoseSkeletonNode);\n assignFathersForSkeletonTree(\u0026g_core-\u003eopenwormWrapper.bindPoseSkeletonNode);\n //load sdf \n string sdf_file_31_0 = config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\" + OW_DATA_VERSION + \"\\\\animation_\"+ std::to_string(OW_SKINNING_NUM_BONES)+\"\\\\SDF__31S_00000.sdf\";\n vector\u003cfloat\u003e sdf_loaded;\n g_core-\u003eopenwormWrapper.loadSDFFromFile(sdf_file_31_0, sdf_loaded);\n //load quaternions\n vector\u003cvector\u003cglm::quat\u003e \u003e source;\n source.reserve(OW_MAX_WORM_ITERATIONS);\n while (timeStep \u003c OW_MAX_WORM_ITERATIONS) {\n\t //load skinning data\n\t string quaternionFile = config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\" + OW_DATA_VERSION + \"\\\\animation_\"+ std::to_string(OW_SKINNING_NUM_BONES)+\"_bindpose\"+ std::to_string(OW_BINDPOSE_TIMESTEP) +\"\\\\quaternion_\" + std::to_string(OW_SKINNING_NUM_BONES) + \"S_\" + g_core-\u003eexportWrapper.PadNumber(timeStep, 4) + \".qua\";\n\t ifstream isq(quaternionFile);\n\t assert(isq.good());\n\t source.push_back(vector\u003cglm::quat\u003e());\n\t g_core-\u003eopenwormWrapper.ImportQuaternion(source.back(), isq);\n\n\t timeStep += timeIncrease;\n }\t\t\t\t \n //postprocess quaternions, smoth them with SLERP radius\n SM::SmoothQuaternions(source, 130);\n //output quaternions\n timeStep = OW_START_WORM_ITERATION;\n for (int i = 0; i \u003c source.size(); i++) {\n\t //save quaternions to file\n\t string quaternionFile = config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\" + OW_DATA_VERSION + \"\\\\animation_\"+ std::to_string(OW_SKINNING_NUM_BONES)+\"_bindpose\"+ std::to_string(OW_BINDPOSE_TIMESTEP) +\"\\\\postprocessed\\\\quaternion_post_\" + std::to_string(OW_SKINNING_NUM_BONES) + \"S_\" + g_core-\u003eexportWrapper.PadNumber(timeStep, 4) + \".qua\";\n\t ofstream outputQ(quaternionFile);\n\n\t for (int q = 0; q \u003c source[i].size(); q++) {\n\t\t glm::quat quat = source[i][q];\n\t\t outputQ \u003c\u003c quat.w \u003c\u003c \" \" \u003c\u003c quat.x \u003c\u003c \" \" \u003c\u003c quat.y \u003c\u003c \" \" \u003c\u003c quat.z \u003c\u003c std::endl;\n\t }\n\t //save matrix to file\n\t string matrixFile = config.projectDir + \"Models\\\\OpenWorm\\\\Export\\\\export_\" + OW_DATA_VERSION + \"\\\\animation_\"+ std::to_string(OW_SKINNING_NUM_BONES)+\"_bindpose\"+ std::to_string(OW_BINDPOSE_TIMESTEP) +\"\\\\anchored2\\\\matrix_anchored_\" + std::to_string(OW_SKINNING_NUM_BONES) + \"S_\" + g_core-\u003eexportWrapper.PadNumber(timeStep, 4) + \".mat\";\n\t ofstream outputM(matrixFile);\n\t vector\u003cglm::mat4\u003e matrices;\n\t g_core-\u003eopenwormWrapper.performSDFBasedTransformationsInSkeletonTree(\u0026g_core-\u003eopenwormWrapper.bindPoseSkeletonNode, \u0026source[i], \u0026matrices, sdf_loaded, 9.81, glm::vec3(0, 0, 1));\n\t // anchor middle node of the skeleton, translate all the other nodes\n\t g_core-\u003eopenwormWrapper.anchorSkeletonDuringSkinning(\u0026g_core-\u003eopenwormWrapper.bindPoseSkeletonNode, \u0026matrices, 22);\n\n\t for (int m = 0; m \u003c matrices.size(); m++) {\n\t\t for (int j = 0; j \u003c 4; j++) {\n\t\t\t for (int k = 0; k \u003c 4; k++) {\n\t\t\t\t outputM \u003c\u003c matrices[m][j][k] \u003c\u003c \" \";\n\t\t\t }\n\t\t\t outputM \u003c\u003c std::endl;\n\t\t }\n\t\t outputM \u003c\u003c std::endl;\n\t }\n\n\t timeStep += timeIncrease;\n }\n```\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenworm%2Fskeletonextraction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenworm%2Fskeletonextraction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenworm%2Fskeletonextraction/lists"}