@@ -104,6 +104,7 @@ std::tuple<ParameterTransform, ParameterLimits> loadModelDefinitionFromStream(
104104
105105 pt = parseParameterTransform (data, skeleton);
106106 pt.parameterSets = parseParameterSets (data, pt);
107+ pt.poseConstraints = parsePoseConstraints (data, pt);
107108 pl = parseParameterLimits (data, skeleton, pt);
108109
109110 return res;
@@ -435,4 +436,159 @@ std::tuple<ParameterTransform, ParameterLimits> loadModelDefinition(
435436 return loadModelDefinitionFromStream (inputStream, skeleton);
436437}
437438
439+ std::string writeParameterTransform (
440+ const ParameterTransform& parameterTransform,
441+ const Skeleton& skeleton) {
442+ std::ostringstream oss;
443+
444+ // Write each joint parameter definition
445+ for (size_t iJoint = 0 ; iJoint < skeleton.joints .size (); ++iJoint) {
446+ const auto & joint = skeleton.joints [iJoint];
447+
448+ for (size_t iParam = 0 ; iParam < kParametersPerJoint ; ++iParam) {
449+ const size_t jointParamIndex = iJoint * kParametersPerJoint + iParam;
450+
451+ // Skip inactive joint parameters
452+ if (!parameterTransform.activeJointParams [jointParamIndex]) {
453+ continue ;
454+ }
455+
456+ // Write joint parameter name
457+ oss << joint.name << " ." << kJointParameterNames [iParam] << " = " ;
458+
459+ // Collect all model parameters that influence this joint parameter
460+ std::vector<std::pair<size_t , float >> influences;
461+ for (Eigen::Index iModelParam = 0 ; iModelParam < parameterTransform.transform .cols ();
462+ ++iModelParam) {
463+ const float weight = parameterTransform.transform .coeff (jointParamIndex, iModelParam);
464+ if (weight != 0 .0f ) {
465+ influences.emplace_back (iModelParam, weight);
466+ }
467+ }
468+
469+ // Write the offset if it exists
470+ const float offset = parameterTransform.offsets [jointParamIndex];
471+ bool needsPlus = false ;
472+
473+ if (!influences.empty ()) {
474+ for (size_t i = 0 ; i < influences.size (); ++i) {
475+ if (i > 0 ) {
476+ oss << " + " ;
477+ }
478+ const auto [modelParamIndex, weight] = influences[i];
479+ oss << weight << " *" << parameterTransform.name [modelParamIndex];
480+ }
481+ needsPlus = true ;
482+ }
483+
484+ if (offset != 0 .0f ) {
485+ if (needsPlus) {
486+ oss << " + " ;
487+ }
488+ oss << offset;
489+ }
490+
491+ oss << " \n " ;
492+ }
493+ }
494+
495+ return oss.str ();
496+ }
497+
498+ std::string writeParameterSets (const ParameterSets& parameterSets) {
499+ std::ostringstream oss;
500+
501+ for (const auto & [name, paramSet] : parameterSets) {
502+ oss << " parameterset " << name;
503+
504+ // Write all active parameters in the set
505+ for (size_t i = 0 ; i < paramSet.size (); ++i) {
506+ if (paramSet.test (i)) {
507+ // We need the parameter name, but we don't have access to parameterTransform here
508+ // This is a limitation - we'll need to pass it in
509+ oss << " param_" << i;
510+ }
511+ }
512+ oss << " \n " ;
513+ }
514+
515+ return oss.str ();
516+ }
517+
518+ std::string writePoseConstraints (const PoseConstraints& poseConstraints) {
519+ std::ostringstream oss;
520+
521+ for (const auto & [name, constraint] : poseConstraints) {
522+ oss << " poseconstraints " << name;
523+
524+ // Write all parameter=value pairs
525+ for (const auto & [paramIndex, value] : constraint.parameterIdValue ) {
526+ // We need the parameter name, but we don't have access to parameterTransform here
527+ oss << " param_" << paramIndex << " =" << value;
528+ }
529+ oss << " \n " ;
530+ }
531+
532+ return oss.str ();
533+ }
534+
535+ std::string writeModelDefinition (
536+ const Skeleton& skeleton,
537+ const ParameterTransform& parameterTransform,
538+ const ParameterLimits& parameterLimits) {
539+ std::ostringstream oss;
540+
541+ // Write header
542+ oss << " Momentum Model Definition V1.0\n\n " ;
543+
544+ // Write ParameterTransform section
545+ if (!parameterTransform.name .empty ()) {
546+ oss << " [ParameterTransform]\n " ;
547+ oss << writeParameterTransform (parameterTransform, skeleton);
548+ oss << " \n " ;
549+ }
550+
551+ // Write ParameterSets section
552+ if (!parameterTransform.parameterSets .empty ()) {
553+ oss << " [ParameterSets]\n " ;
554+ for (const auto & [name, paramSet] : parameterTransform.parameterSets ) {
555+ oss << " parameterset " << name;
556+
557+ // Write all active parameters in the set
558+ for (size_t i = 0 ; i < paramSet.size () && i < parameterTransform.name .size (); ++i) {
559+ if (paramSet.test (i)) {
560+ oss << " " << parameterTransform.name [i];
561+ }
562+ }
563+ oss << " \n " ;
564+ }
565+ oss << " \n " ;
566+ }
567+
568+ // Write PoseConstraints section
569+ if (!parameterTransform.poseConstraints .empty ()) {
570+ oss << " [PoseConstraints]\n " ;
571+ for (const auto & [name, constraint] : parameterTransform.poseConstraints ) {
572+ oss << " poseconstraints " << name;
573+
574+ // Write all parameter=value pairs
575+ for (const auto & [paramIndex, value] : constraint.parameterIdValue ) {
576+ if (paramIndex < parameterTransform.name .size ()) {
577+ oss << " " << parameterTransform.name [paramIndex] << " =" << value;
578+ }
579+ }
580+ oss << " \n " ;
581+ }
582+ oss << " \n " ;
583+ }
584+
585+ // Write ParameterLimits section using existing function
586+ if (!parameterLimits.empty ()) {
587+ oss << " [ParameterLimits]\n " ;
588+ oss << writeParameterLimits (parameterLimits, skeleton, parameterTransform);
589+ }
590+
591+ return oss.str ();
592+ }
593+
438594} // namespace momentum
0 commit comments