<commit_before>AE = Artificial.Everywhere
AS = Artificial.Spectrum

class AS.AnimatedMesh extends AS.RenderObject
  constructor: (@options) ->
    super arguments...

    @data = new ReactiveField @options.data
    @texture = new ReactiveField @options.texture

    # Load data and texture if provided with their URLs.
    if @options.dataUrl
      $.getJSON @options.dataUrl, (data) =>
        @data data

    if @options.textureUrl
      texture = new THREE.TextureLoader().load @options.textureUrl
      texture.magFilter = THREE.NearestFilter
      texture.minFilter = THREE.NearestFilter
      @texture texture

    # Create material from texture.
    @_material = @options.material or new THREE.MeshBasicMaterial
      transparent: true
      side: THREE.DoubleSide
      depthWrite: false

    @_depthMaterial = new THREE.MeshDepthMaterial
      depthPacking: THREE.RGBADepthPacking
      alphaTest: 0.5

    @materials = new AE.ReactiveWrapper
      main: @_material
      depth: @_depthMaterial

    # Reactively update the texture.
    @autorun (computation) =>
      @_material.map = @texture()
      @_material.needsUpdate = true

      @_depthMaterial.map = @texture()
      @_depthMaterial.needsUpdate = true

      @materials.updated()

    # Create creature objects.
    @creature = new ComputedField =>
      return unless data = @data()

      new AS.Creature data
    ,
      true

    @creatureManager = new ComputedField =>
      return unless creature = @creature()
      data = @data()

      creatureManager = new AS.CreatureManager creature
      @_autoBlendSet = false

      creatureManager.SetTimeScale @options.dataFPS

      for name, animationData of data.animation
        creatureManager.AddAnimation new AS.CreatureAnimation data, name

      creatureManager
    ,
      true

    @creatureRenderer = new ComputedField =>
      return unless creature = @creature()
      return unless creatureManager = @creatureManager()
      materials = @materials()

      creatureRenderer = new AS.CreatureRenderer creature, creatureManager, materials.main
      creatureRenderer.renderMesh.castShadow = @options.castShadow
      creatureRenderer.renderMesh.receiveShadow = @options.receiveShadow
      creatureRenderer.renderMesh.customDepthMaterial = materials.depth

      # Add/replace the render mesh as a child.
      @remove @_currentRenderMesh
      @_currentRenderMesh = creatureRenderer.renderMesh
      @add @_currentRenderMesh

      creatureRenderer
    ,
      true

    # Reactively play animations.
    @currentAnimationName = new ReactiveField()
    @blendTime = new ReactiveField 0

    @autorun (computation) =>
      return unless creatureManager = @creatureManager()
      return unless creatureRenderer = @creatureRenderer()

      return unless animationName = @currentAnimationName()
      return unless creatureManager.GetAnimation animationName if animationName

      if @_autoBlendSet
        blendRate = 1 / @blendTime() / @options.playbackFPS
        creatureManager.AutoBlendTo animationName, blendRate

      else
        creatureManager.SetActiveAnimationName animationName
        creatureManager.SetAutoBlending true
        @_autoBlendSet = true

      creatureManager.SetShouldLoop true
      creatureManager.SetIsPlaying true
      creatureManager.RunAtTime 0
      
      # Do the first update.
      creatureRenderer.UpdateData()

    @_accumulatedTime = 0

  destroy: ->
    super arguments...

    @creature.stop()
    @creatureManager.stop()
    @creatureRenderer.stop()

  animationNames: ->
    _.keys @data()?.animation

  update: (appTime, updateData = true) ->
    @_accumulatedTime += appTime.elapsedAppTime

    timeForUpdate = 1 / @options.playbackFPS

    while @_accumulatedTime > timeForUpdate
      @_accumulatedTime -= timeForUpdate

      @creatureManager()?.Update timeForUpdate
      @creatureRenderer()?.UpdateData() if updateData

  syncAnimationTo: (otherAnimatedMesh) ->
    return unless otherCreatureManager = otherAnimatedMesh.creatureManager()
    animationName = otherCreatureManager.GetActiveAnimationName()
    runTime = otherCreatureManager.getRunTime()<commit_msg>More stable animation update handling.<commit_after>AE = Artificial.Everywhere
AS = Artificial.Spectrum

class AS.AnimatedMesh extends AS.RenderObject
  constructor: (@options) ->
    super arguments...

    @data = new ReactiveField @options.data
    @texture = new ReactiveField @options.texture

    # Load data and texture if provided with their URLs.
    if @options.dataUrl
      $.getJSON @options.dataUrl, (data) =>
        @data data

    if @options.textureUrl
      texture = new THREE.TextureLoader().load @options.textureUrl
      texture.magFilter = THREE.NearestFilter
      texture.minFilter = THREE.NearestFilter
      @texture texture

    # Create material from texture.
    @_material = @options.material or new THREE.MeshBasicMaterial
      transparent: true
      side: THREE.DoubleSide
      depthWrite: false

    @_depthMaterial = new THREE.MeshDepthMaterial
      depthPacking: THREE.RGBADepthPacking
      alphaTest: 0.5

    @materials = new AE.ReactiveWrapper
      main: @_material
      depth: @_depthMaterial

    # Reactively update the texture.
    @autorun (computation) =>
      @_material.map = @texture()
      @_material.needsUpdate = true

      @_depthMaterial.map = @texture()
      @_depthMaterial.needsUpdate = true

      @materials.updated()

    # Create creature objects.
    @creature = new ComputedField =>
      return unless data = @data()

      new AS.Creature data
    ,
      true

    @creatureManager = new ComputedField =>
      return unless creature = @creature()
      data = @data()

      creatureManager = new AS.CreatureManager creature
      @_autoBlendSet = false

      creatureManager.SetTimeScale @options.dataFPS

      for name, animationData of data.animation
        creatureManager.AddAnimation new AS.CreatureAnimation data, name

      creatureManager
    ,
      true

    @creatureRenderer = new ComputedField =>
      return unless creature = @creature()
      return unless creatureManager = @creatureManager()
      materials = @materials()

      creatureRenderer = new AS.CreatureRenderer creature, creatureManager, materials.main
      creatureRenderer.renderMesh.castShadow = @options.castShadow
      creatureRenderer.renderMesh.receiveShadow = @options.receiveShadow
      creatureRenderer.renderMesh.customDepthMaterial = materials.depth

      # Add/replace the render mesh as a child.
      @remove @_currentRenderMesh
      @_currentRenderMesh = creatureRenderer.renderMesh
      @add @_currentRenderMesh

      creatureRenderer
    ,
      true

    # Reactively play animations.
    @currentAnimationName = new ReactiveField()
    @blendTime = new ReactiveField 0

    @autorun (computation) =>
      return unless creatureManager = @creatureManager()
      return unless creatureRenderer = @creatureRenderer()

      return unless animationName = @currentAnimationName()
      return unless creatureManager.GetAnimation animationName if animationName

      if @_autoBlendSet
        blendRate = 1 / @blendTime() / @options.playbackFPS
        creatureManager.AutoBlendTo animationName, blendRate

      else
        creatureManager.SetActiveAnimationName animationName
        creatureManager.SetAutoBlending true
        @_autoBlendSet = true

      creatureManager.SetShouldLoop true
      creatureManager.SetIsPlaying true
      creatureManager.RunAtTime 0
      
      # Do the first update.
      creatureRenderer.UpdateData()

    @_accumulatedTime = 0

  destroy: ->
    super arguments...

    @creature.stop()
    @creatureManager.stop()
    @creatureRenderer.stop()

  animationNames: ->
    _.keys @data()?.animation

  update: (appTime, updateData = true) ->
    @_accumulatedTime += appTime.elapsedAppTime

    timeForUpdate = 1 / @options.playbackFPS

    return unless @_accumulatedTime > timeForUpdate

    @creatureManager()?.Update @_accumulatedTime
    @creatureRenderer()?.UpdateData() if updateData

    @_accumulatedTime = 0

  syncAnimationTo: (otherAnimatedMesh) ->
    return unless otherCreatureManager = otherAnimatedMesh.creatureManager()
    animationName = otherCreatureManager.GetActiveAnimationName()
    runTime = otherCreatureManager.getRunTime()
{'fraction_non_alphanumeric': 0.07690461907618476, 'fraction_numerical': 0.001799640071985603, 'mean_word_length': 3.1165432098765433, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2010268', 'n_tokens_mistral': 2287, 'n_tokens_neox': 2195, 'n_words': 548}