Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improving the performance of animated sprites in three.js

Right now I'm working on an online game and have decided to use webgl rather than HTML5's canvas for performance reasons. I'm using the three.js framework and my intention is to have moving animated sprites. The sprites themselves are placed on spritesheets and I use the UVOffset and UVScale to use the right art and switch the Sprite's art during time passing by. I was wondering whether there's a way to improve the performance of this code, because right now it starts to slow down at around 300 "players" on the field at the same time.

Regards

The following is the most important part of my code:

var image = THREE.ImageUtils.loadTexture( "img/idlew.png" );

  function addPlayer(){
    var mesh = new THREE.Sprite({map:image});//, affectedByDistance: false, useScreenCoordinates: false});
    images.push(mesh);
    mesh.position.set(Math.floor(Math.random() * 500), Math.floor(Math.random() * 500), 10);
    scene.add(mesh);
    mesh.uvScale.x = 96/1152;
    mesh.scale.x = 0.1;
    mesh.scale.y = 0.1;
  }


var imgBackground  = new THREE.MeshLambertMaterial({
      map:THREE.ImageUtils.loadTexture('img/grass.jpg')
  });


   var background = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000),imgBackground);


  scene.add(background);



  scene.add(camera);
camera.rotation.x = -(Math.PI/2);
scene.add(new THREE.AmbientLight(0xFFFFFF));

addPlayer();

  renderer.render(scene, camera);
  var moveUp = false;
  tick();
  var ticker = 0;
  var usedOne = 0;

  function tick(){
    ticker++;
    if(ticker%10==0){
      for (var i = 0; i < images.length; i++) {
        images[i].uvOffset.x = usedOne * 0.0835;
      };
        usedOne++;
        if(usedOne == 12) usedOne = 0;
        addPlayer();
        addPlayer();
        addPlayer();
        console.log(images.length);
    }
    requestAnimationFrame( tick );

      renderer.render(scene, camera);
  }
like image 697
Kristof Avatar asked Jul 01 '12 17:07

Kristof


1 Answers

I have written an example of code to display an animated texture, live example at:

http://stemkoski.github.com/Three.js/Texture-Animation.html

with source available at:

http://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Texture-Animation.html

The useful part is a function I wrote to handle the offsets automatically. The function (extracted from the link above) is as follows:

function TextureAnimator(texture, tilesHoriz, tilesVert, numTiles, tileDispDuration) 
{   
    // note: texture passed by reference, will be updated by the update function.

    this.tilesHorizontal = tilesHoriz;
    this.tilesVertical = tilesVert;

    // how many images does this spritesheet contain?
    //  usually equals tilesHoriz * tilesVert, but not necessarily,
    //  if there at blank tiles at the bottom of the spritesheet. 
    this.numberOfTiles = numTiles;
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
    texture.repeat.set( 1 / this.tilesHorizontal, 1 / this.tilesVertical );

    // how long should each image be displayed?
    this.tileDisplayDuration = tileDispDuration;

    // how long has the current image been displayed?
    this.currentDisplayTime = 0;

    // which image is currently being displayed?
    this.currentTile = 0;

    this.update = function( milliSec )
    {
        this.currentDisplayTime += milliSec;
        while (this.currentDisplayTime > this.tileDisplayDuration)
        {
            this.currentDisplayTime -= this.tileDisplayDuration;
            this.currentTile++;
            if (this.currentTile == this.numberOfTiles)
                this.currentTile = 0;
            var currentColumn = this.currentTile % this.tilesHorizontal;
            texture.offset.x = currentColumn / this.tilesHorizontal;
            var currentRow = Math.floor( this.currentTile / this.tilesHorizontal );
            texture.offset.y = currentRow / this.tilesVertical;
        }
    };
}       

You can initialize the material using (for example):

var runnerTexture = new THREE.ImageUtils.loadTexture( 'images/run.png' );
// a texture with 10 frames arranged horizontally, display each for 75 millisec
annie = new TextureAnimator( runnerTexture, 10, 1, 10, 75 ); 
var runnerMaterial = new THREE.MeshBasicMaterial( { map: runnerTexture } );

and update it before each render using:

var delta = clock.getDelta(); 
annie.update(1000 * delta);

Hope this helps!

like image 88
Stemkoski Avatar answered Oct 01 '22 14:10

Stemkoski