Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions sceneview/src/main/java/io/github/sceneview/node/ModelNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.github.sceneview.model.lightEntities
import io.github.sceneview.model.model
import io.github.sceneview.model.renderableEntities
import io.github.sceneview.utils.intervalSeconds
import kotlin.math.abs

/**
* Create the ModelNode from a loaded model instance.
Expand Down Expand Up @@ -102,7 +103,11 @@ open class ModelNode(
) : Node(engine = modelInstance.engine, entity = entity),
ChildNode

data class PlayingAnimation(val startTime: Long = System.nanoTime(), val loop: Boolean = true)
data class PlayingAnimation(
val startTime: Long = System.nanoTime(),
var speed: Float = 1f,
val loop: Boolean = true
)

val renderableNodes = modelInstance.renderableEntities.map {
RenderableNode(modelInstance, it)
Expand Down Expand Up @@ -225,21 +230,46 @@ open class ModelNode(
* animation definition. Uses `TransformManager`.
*
* @param animationIndex Zero-based index for the `animation` of interest.
* @param speed The rate at which the `animation` plays. Reverses the `animation` if negative.
* Pauses the `animation` if zero.
* @param loop Specifies if the `animation` should repeat forever.
*
* @see Animator.getAnimationCount
*/
fun playAnimation(animationIndex: Int, loop: Boolean = true) {
fun playAnimation(animationIndex: Int, speed: Float = 1f, loop: Boolean = true) {
if (animationIndex < animationCount) {
playingAnimations[animationIndex] = PlayingAnimation(loop = loop)
playingAnimations[animationIndex] = PlayingAnimation(speed = speed, loop = loop)
}
}

/**
* @see playAnimation
* @see Animator.getAnimationName
*/
fun playAnimation(animationName: String, loop: Boolean = true) {
animator.getAnimationIndex(animationName)?.let { playAnimation(it, loop) }
fun playAnimation(animationName: String, speed: Float = 1f, loop: Boolean = true) {
animator.getAnimationIndex(animationName)?.let { playAnimation(it, speed, loop) }
}

/**
* Sets the rate at which the `animation` is played.
*
* @param animationIndex Zero-based index for the `animation` of interest.
* @param speed The rate at which the `animation` plays. Reverses the `animation` if negative.
* Pauses the `animation` if zero.
* @see playAnimation
*/
fun setAnimationSpeed(animationIndex: Int, speed: Float) {
if (animationIndex < animationCount) {
playingAnimations[animationIndex]?.speed = speed
}
}

/**
* @see setAnimationSpeed
* @see Animator.getAnimationName
*/
fun setAnimationSpeed(animationName: String, speed: Float) {
animator.getAnimationIndex(animationName)?.let { setAnimationSpeed(it, speed) }
}

fun stopAnimation(animationIndex: Int) {
Expand Down Expand Up @@ -375,18 +405,31 @@ open class ModelNode(
super.onFrame(frameTimeNanos)

model.popRenderable()
applyAnimations(frameTimeNanos)
animator.updateBoneMatrices()
}

private fun applyAnimations(frameTimeNanos: Long) {
playingAnimations.forEach { (index, animation) ->
if (animation.speed == 0f) return@forEach

animator.let { animator ->
val elapsedTimeSeconds = frameTimeNanos.intervalSeconds(animation.startTime)
animator.applyAnimation(index, elapsedTimeSeconds.toFloat())
val adjustedTimeSeconds = elapsedTimeSeconds.toFloat() * abs(animation.speed)
val animationDuration = animator.getAnimationDuration(index)
val animationTime: Float = if (animation.speed > 0) {
adjustedTimeSeconds
} else {
animationDuration - adjustedTimeSeconds
}

if (!animation.loop && elapsedTimeSeconds >= animator.getAnimationDuration(index)) {
animator.applyAnimation(index, animationTime)

if (!animation.loop && adjustedTimeSeconds >= animationDuration) {
playingAnimations.remove(index)
}
}
}
animator.updateBoneMatrices()
}
}

Expand Down