如何使用 `rgl` 和 `animation` 使用加速度计数据旋转手对象

How to rotate hand object with accelerometer data using `rgl` and `animation`

假设我有一个线框、3D 对象,例如 https://free3d.com/3d-model/freerealsichand-85561.html

假设我想将所述物体的手腕固定为加速度计手表的读数(例如,通过航位推算获取位置)。

如何在 rgl 中绘制这样的 3-D 图像?

如何根据加速度计数据的变化旋转/平移所述图像?

奖金:我如何玩这个 activity 的 animation。 (例如,图书馆 animation

加速度计是 3-DOF:x-acc、y-acc、z-acc,没有任何其他数据。

link 包括几种格式的手,但 rgl 目前无法阅读它们。它最接近 .OBJ 格式,但无法读取该文件中记录的法线或纹理,因此失败。您可能想尝试更新 readOBJ 以支持更多 OBJ 格式;或者,您可以像这样删除该信息:

hand <- readLines("hand.OBJ")
poly <- grepl("^f ", hand)
hand[poly] <- gsub("/[^ ]+", "", hand[poly])
handmesh <- readOBJ(textConnection(hand))

这将给出一些警告,因为它忽略了该文件的其他部分,但它会生成一个 rgl 可以处理的对象。你可以这样画:

shade3d(handmesh, col = "pink")

要平移和旋转形状,请使用 userMatrix 上的 translate3drotate3d 函数,例如

par3d(userMatrix = rotate3d(par3d("userMatrix"), 0.1, 1,0,0))

将手绕 x 轴旋转 0.1 弧度。 (如果您想要永久更改,也可以旋转 handmesh 本身。)

您需要花一些时间弄清楚手腕的坐标是什么,并将您的 3-DOF 数据转换为正确的坐标系。

您不需要 animation 动画;请参阅 ?rgl::play3d 中的示例。例如,要绕 x 轴旋转手,您可以使用

open3d()
shade3d(handmesh, col = "pink")
play3d(spin3d(axis = c(1, 0, 0)))

根据@user2554330提供的答案,我深挖了细节。

rgl::readOBJ有点不完善,我把这件事通知了R-forge:rgl:https://r-forge.r-project.org/tracker/index.php?func=detail&aid=6543&group_id=234&atid=949

# rgl::readOBJ
beginT = Sys.time();
hand <- readLines("hand.OBJ")
poly <- grepl("^f ", hand)
hand[poly] <- gsub("/[^ ]+", "", hand[poly])
handmesh <- readOBJ(textConnection(hand))
endT = Sys.time(); print(endT-beginT);
# Time difference of 1.555696964263916 secs

我的 hack 有四个功能:

#' Convert String Matrix to Numeric Matrix
#'
#' @param m string matrix
#'
#' @return numeric matrix
#' @export

numericMatrix = function(m)
{

  m = as.matrix(m);
  mdim = dim(m);

  nm <- mapply(m, FUN=as.numeric);
  matrix(data=nm, nrow=mdim[1], ncol=mdim[2])

}


#' Convert String List of "f" to numeric multi-dimensional array
#'
#' @param fobj 
#'
#' @return fn, multidimension array of rows, cols, and values
#' @export
#'

numericF = function(fobj)
{

  rdim = dim(fobj)[1];  # rows
  cdim = dim(fobj)[2];  # cols
  ddim = 3;

  fn = array(NA,dim=c(rdim,cdim,ddim));


  for(rn in 1:rdim)
    {
    for(cn in 1:cdim)
      {
      rcdat = fobj[rn,cn];
      fdat = as.numeric(unlist(strsplit(rcdat,"/",fixed=T))); # basic numeric case
      fn[rn,cn,] = fdat;
      }
    }



  #fn[1:10,1:4,1:3]

  fn;


    }



#' Internally equivalent to rgl 'readOBJ'
#'
#' @param rawobj Raw object read in using parseFileOBJ
#' 
#' rgl::readOBJ
#' hand <- readLines("hand.OBJ")
#' poly <- grepl("^f ", hand)
#' hand[poly] <- gsub("/[^ ]+", "", hand[poly])
#' handmesh <- readOBJ(textConnection(hand))
#' 
#' mymesh = buildBasicMeshFromOBJ(parseFileOBJ("hand.OBJ"));
#' 
#' These appear to be equivalent in the basic form
#'
#' @return list object equivalent to readOBJ output
#' @export
#'

buildBasicMeshFromOBJ <- function(rawobj)
{

  mesh = list();
      vb = as.matrix(rawobj$v);
      vb = cbind(vb,1);
    mesh$vb = t(vb);
    mesh$it = matrix(0,nrow=3,ncol=0);
    mesh$primitivetype = "triangle";
    mesh$ib = t(as.matrix(rawobj$fn[,,1]));

    attr(mesh,"class") = c("mesh3d","shape3d");

  mesh;  
}

#' Parse file of type OBJ
#'
#' Specification for OBJ format can be found here:
#' http://www.martinreddy.net/gfx/3d/OBJ.spec
#' 
#' Requires gdata::trim
#' 
#' @param filename 
#'
#' @return list 'rawobj' with all details from the file
#' @export
#'
#' @examples
#' rawobj = parseFileOBJ("hand.obj");
parseFileOBJ <- function(filename)
{



  linestxt <- readLines(filename)                 # read all lines
  linestxt = gsub("\s+"," ",gdata::trim(linestxt));  # replace multiple spaces with single space
  linesobj = strsplit(linestxt," ",fixed=T);      # split on spaces


  rawobj = list();
  rawobj$lines = c();
  rawobj$comments = c();



  for(i in 1:length(linesobj))
  {
    hir = gdata::trim(linesobj[[i]]);
    key = hir[1]; 
    rdat = hir[-1]; 
    rlen = length(rdat);
    rstr = paste(rdat,collapse=" ");
    if (key=="#") {
      rawobj$lines = c(rawobj$lines,"comments");
      rawobj$comments = c(rawobj$comments,rstr);
      } else { rawobj$lines = c(rawobj$lines,key); 
              if(is.null(rawobj[[key]])) { rawobj[[key]] = rdat; } else 
                {  rawobj[[key]] = rbind(rawobj[[key]],rdat);}
      }
  }


  if(!is.null(rawobj[["v"]])) { rawobj[["v"]] = numericMatrix(rawobj[["v"]]);  }
  if(!is.null(rawobj[["vn"]])) { rawobj[["vn"]] = numericMatrix(rawobj[["vn"]]);  }
  if(!is.null(rawobj[["vt"]])) { rawobj[["vt"]] = numericMatrix(rawobj[["vt"]]);  }

  # "f" could be numeric (f 1 2 3 4) or (f -4 -3 -2 -1) or references to v,vn,vt (f 1/1/1 2/2/2 3/3/3 4/4/4) or (f v/vt/vn v/vt/vn v/vt/vn v/vt/vn) or (f 1//1 2//2 3//3 4//4)
  # rdat = c("1102/9904/4404","2981/9909/4404","3943/9910/4404","2854/9905/4404");
  # rdat = c('1//1','2//2','3//3',4//4');

  if(!is.null(rawobj[["f"]])) { rawobj[["fn"]] = numericF(rawobj[["f"]]);  }



  rawobj;

}

基本用法:

# Takes longer, but processes "all" info in the file
beginT = Sys.time();
rawobj = parseFileOBJ("hand.OBJ");
endT = Sys.time(); print(endT-beginT); beginT = endT;
# Time difference of 6.9523830413818359 secs
mymesh = buildBasicMeshFromOBJ(rawobj);
endT = Sys.time(); print(endT-beginT);
# Time difference of 0.0030009746551513672 secs

rawobject 具有 所有 根据此规范在 OBJ 文件中找到的信息:http://www.martinreddy.net/gfx/3d/OBJ.spec