# test performance of display of heavy scene involving multiple interactive
# objects, on example of 1000 spheres

#Category: Visualization
#Title: Display of complex scene and animation

pload MODELING
pload VISUALIZATION

vinit View1 w=1024 h=1024
vclear
vdefaults -autoTriang 0
vrenderparams -stats basic

# parameter NB defines number of spheres by each coordinate
set NB 10
puts "Creating [expr $NB * $NB * $NB] spheres..."
set slist {}
for {set i 0} {$i < $NB} {incr i} {
  for {set j 0} {$j < $NB} {incr j} {
    for {set k 0} {$k < $NB} {incr k} {
      psphere s$i$j$k 1.
      lappend slist s$i$j$k
      ttranslate s$i$j$k 3.*$i 3.*$j 3.*$k
    }
  }
}
eval compound $slist c
incmesh c 0.006

puts "Measuring FPS of display of spheres as separate objects..."
vaxo
eval vdisplay -dispMode 1 $slist
vfit

# measure FPS
puts [set fps_separate [vfps]]
vclear

puts "Measuring FPS of display of spheres as single object..."
vdisplay -dispMode 1 c

# measure FPS
puts [set fps_compound [vfps]]
vclear

# redisplay individual spheres, trying to avoid unnecessary internal updates
eval vdisplay -dispMode 1 $slist

# auxiliary procedure to make random update of variable
proc upd {theValueName theDeltaName theTime theToRand} {
  upvar $theValueName aValue
  upvar $theDeltaName aDelta

  # set colors to corner spheres
  if { $theToRand == 1 } {
    set aValue [expr $aValue + $aDelta * $theTime / 100.0]
    set aDelta [expr 0.5 * (rand() - 0.5)]
    return $aValue
  }

  set aRes [expr $aValue + $aDelta * $theTime / 100.0]
}

# move corner spheres in cycle
proc animateSpheres {{theDuration 10.0}} {
  set nb [expr $::NB - 1]

  # set colors to corner spheres
  for {set i 0} {$i < $::NB} {incr i $nb} {
    for {set j 0} {$j < $::NB} {incr j $nb} {
      for {set k 0} {$k < $::NB} {incr k $nb} {
        # mark animated spheres mutable for faster updates
        uplevel #0 vdisplay -dispMode 1 -mutable s$i$j$k
#       vaspects -noupdate s$i$j$k -setcolor red -setmaterial plastic
        uplevel #0 vaspects -noupdate s$i$j$k -setcolor red
        set x$i$j$k  0.0
        set y$i$j$k  0.0
        set z$i$j$k  0.0
        set dx$i$j$k 0.0
        set dy$i$j$k 0.0
        set dz$i$j$k 0.0
      }
    }
  }

  set aDuration 0.0
  set aPrevRand 0.0
  set aTimeFrom [clock clicks -milliseconds]
  uplevel #0 chrono anAnimTimer reset
  uplevel #0 chrono anAnimTimer start
  set toRand 1
  for {set aFrameIter 1} { $aFrameIter > 0 } {incr aFrameIter} {
    set aCurrTime [expr [clock clicks -milliseconds] - $aTimeFrom]
    if { $aCurrTime >= [expr $theDuration * 1000.0] } {
      puts "Nb Frames: $aFrameIter"
      puts "Duration:  [expr $aCurrTime * 0.001] s"
      set fps [expr ($aFrameIter - 1) / ($aDuration * 0.001) ]
      puts "FPS:       $fps"
      uplevel #0 chrono anAnimTimer stop
      uplevel #0 chrono anAnimTimer show
      return $fps
    }

    set aRandTime [expr $aCurrTime - $aPrevRand]
    if { $aRandTime > 1000 } {
      set toRand 1
      set aPrevRand $aCurrTime
    }

    #puts "PTS: $aCurrTime ms"
    for {set i 0} {$i < $::NB} {incr i $nb} {
      for {set j 0} {$j < $::NB} {incr j $nb} {
        for {set k 0} {$k < $::NB} {incr k $nb} {
          uplevel #0 vsetlocation -noupdate s$i$j$k [upd x$i$j$k dx$i$j$k $aRandTime $toRand] [upd y$i$j$k dy$i$j$k $aRandTime $toRand] [upd z$i$j$k dz$i$j$k $aRandTime $toRand] 
        }
      }
    }
    uplevel #0 vrepaint
    set aDuration [expr [clock clicks -milliseconds] - $aTimeFrom]
    set toRand 0

    # sleep 1 ms allowing the user to interact with the viewer
    after 1 set waiter 1
    vwait waiter
  }
}

puts "Animating movements of corner spheres (10 sec)..."
puts "(you can interact with the view during the process)"
set fps_animation [animateSpheres 10.0]

puts ""
puts "Performance counters (FPS = \"Frames per second\"):"
puts ""
puts "Spheres as separate interactive objects:"
puts "  Actual FPS: [lindex $fps_separate 1]"
puts "  FPS estimate by CPU load: [expr 1000. / [lindex $fps_separate 3]]"
puts ""
puts "Spheres as one interactive object (compound):"
puts "  Actual FPS: [lindex $fps_compound 1]"
puts "  FPS estimate by CPU load: [expr 1000. / [lindex $fps_compound 3]]"
puts ""
puts "Animation FPS: $fps_animation"
puts ""
puts "Scene contains [lindex [trinfo c] 3] triangles"
puts ""
puts "Print 'animateSpheres 10.0' to restart animation"
