Skip to content

Commit

Permalink
Create animation for the first rotational slider in the model, placeh…
Browse files Browse the repository at this point in the history
…older for creating custom coordinate sliders and arbitrary motions
  • Loading branch information
aymanhab committed Sep 13, 2023
1 parent 6f74ffb commit bf67754
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,39 +343,46 @@ def createExtraAnnotations(self, gltfNode: Node):
gltfNode.extras["opensimType"] = self.currentComponent.getConcreteClassName()

def createAnimationForStateTimeSeries(self,
timeSeriesTable: osim.TimeSeriesTable):
timeSeriesStorage: osim.Storage):
# create a timeSeriesTableVec3 of translations one column per body and
# another timeSeriesTableQuaternion of rotation one per body
timeColumn = timeSeriesTable.getIndependentColumn()
columnLabels = timeSeriesTable.getColumnLabels() #coordinates
times = osim.ArrayDouble()
timeSeriesStorage.getTimeColumn(times)
timeColumn = osim.Vector(times.getAsVector())
stateStorage = osim.Storage()
self.model.formStateStorage(timeSeriesStorage, stateStorage, False)
stateTraj = osim.StatesTrajectory.createFromStatesStorage(self.model, stateStorage)
rotation_arrays = []
translation_arrays = []
bodySet = self.model.getBodySet()
rotation_nparray = np.zeros((21, 4), dtype="float32")
translation_nparray = np.zeros((21, 3), dtype="float32")
firstBody = bodySet.get(0)
for rowIndex in range(len(timeColumn)):
rowTime = timeColumn[rowIndex]
rowValues = timeSeriesTable.getRowAtIndex(rowIndex)

for coordIndex in range(len(columnLabels)):
coord = self.model.getCoordinateSet().get(columnLabels[coordIndex])
coord.setValue(self.modelState, rowValues.to_numpy()[coordIndex], True)
# for body get Transform then convert to translation and rotation
translation = firstBody.getPositionInGround(self.modelState)
rotationAsSimbodyRot = firstBody.getRotationInGround(self.modelState)
rotzSimbodyNotation = rotationAsSimbodyRot.convertRotationToQuaternion()
rotation = [rotzSimbodyNotation.get(3), rotzSimbodyNotation.get(0), rotzSimbodyNotation.get(1), rotzSimbodyNotation.get(2)]
for bodyIndex in range(bodySet.getSize()):
rotation_arrays.append(np.zeros((21, 4), dtype="float32"))
translation_arrays.append(np.zeros((21, 3), dtype="float32"))

rotation_nparray[rowIndex] = rotation
translation_nparray[rowIndex] = translation.to_numpy()

for step in range(stateTraj.getSize()):
nextState = stateTraj.get(step)
self.model.realizePosition(nextState)
for bodyIndex in range(bodySet.getSize()):
nextBody = bodySet.get(bodyIndex)
translation = nextBody.getPositionInGround(nextState).to_numpy()
rotationAsSimbodyRot = nextBody.getRotationInGround(nextState)
rotzSimbodyNotation = rotationAsSimbodyRot.convertRotationToQuaternion()
rotation = [rotzSimbodyNotation.get(1), rotzSimbodyNotation.get(2), rotzSimbodyNotation.get(3), rotzSimbodyNotation.get(0)]
rowTime = timeColumn[step]
for idx in range(4):
rotation_arrays[bodyIndex][step, idx] = rotation[idx]
for idx in range(3):
translation_arrays[bodyIndex][step, idx] = translation[idx]


# create an Animation Node
animation = Animation()
animation.name = timeSeriesTable.getColumnLabel(0)+"_slider"
animation.name = timeSeriesStorage.getColumnLabels().get(1)+"_slider"
self.animations.append(animation)
animationIndex = len(self.animations)-1
# create 2 channels per body one for rotation, other for translation
# keep track of first samplers index then create 2 per body
addTimeStampsAccessor(self.gltf, timeColumn)
addTimeStampsAccessor(self.gltf, timeColumn.to_numpy())
# this is the input to every sampler's input
timeAccessorIndex = len(self.gltf.accessors)-1
# create time sampler
Expand All @@ -387,8 +394,8 @@ def createAnimationForStateTimeSeries(self,
transSampler = AnimationSampler()
rotSampler.input = timeAccessorIndex
transSampler.input = timeAccessorIndex
rotSampler.output = createAccessor(self.gltf, rotation_nparray, 'r')
transSampler.output = createAccessor(self.gltf, translation_nparray, 't')
rotSampler.output = createAccessor(self.gltf, rotation_arrays[bodyIndex], 'r')
transSampler.output = createAccessor(self.gltf, translation_arrays[bodyIndex], 't')
animation.samplers.append(rotSampler)
animation.samplers.append(transSampler)
# Create channels
Expand All @@ -401,9 +408,9 @@ def createAnimationForStateTimeSeries(self,
animation.channels.append(transChannel)

# find target node
body = bodySet.get(bodyIndex)
bodyIndex = body.getMobilizedBodyIndex()
bodyNodeIndex = self.mapMobilizedBodyIndexToNodeIndex[bodyIndex]
nextBody = bodySet.get(bodyIndex)
mobodyIndex = nextBody.getMobilizedBodyIndex()
bodyNodeIndex = self.mapMobilizedBodyIndexToNodeIndex[mobodyIndex]

#Point channels back to samplers
rtarget = AnimationChannelTarget()
Expand All @@ -417,5 +424,5 @@ def createAnimationForStateTimeSeries(self,
ttarget.path = "translation"
transChannel.target = ttarget
transChannel.sampler = transSamplerIndex
# create Accessor for Rotations


22 changes: 9 additions & 13 deletions src/backend/backend/backend/osimConverters/convertOsim2Gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,30 +49,26 @@ def convertOsim2Gltf(osimModelFilePath, geometrySearchPath) :
#find first rotational coordinate, create a motion file varying it along
# its range and pass to decorativeGeometryImp to genrate corresponding animation
coords = model.getCoordinateSet()
timeVec = osim.Vector()
valueVec = osim.Vector()
coordinateSliderTable = osim.TimeSeriesTable()
coordinateSliderStorage = osim.Storage()
coordinateSliderStorage.setInDegrees(False)
for cIndex in range(coords.getSize()):
coordObj = coords.get(cIndex)
moType = coordObj.getMotionType() # want Rotational = 1
coordMin = coordObj.getRangeMin()
coordMax = coordObj.getRangeMax()
if (moType == 1):
timeVec.resize(21)
valueVec.resize(21)
table_time = 2 # 2 sec animation
timeIndex=0
coordinateSliderTable.setColumnLabels([coordObj.getName()])
labels = osim.ArrayStr()
labels.append("time")
labels.append(coordObj.getStateVariableNames().get(0))
coordinateSliderStorage.setColumnLabels(labels)
for sliderTime in np.arange(0, 2.1, 0.1):
coordValue = coordMin + sliderTime/table_time *(coordMax - coordMin)
timeVec.set(timeIndex, sliderTime)
valueVec.set(timeIndex, coordValue)
row = osim.RowVector([coordValue])
coordinateSliderTable.appendRow(sliderTime, row)
timeIndex = timeIndex + 1
row = osim.Vector(1, coordValue)
coordinateSliderStorage.append(sliderTime, row)

break
decorativeGeometryImp.createAnimationForStateTimeSeries(coordinateSliderTable)
decorativeGeometryImp.createAnimationForStateTimeSeries(coordinateSliderStorage)

modelGltf = decorativeGeometryImp.get_GLTF()

Expand Down

0 comments on commit bf67754

Please sign in to comment.