Renderers

Jasper edited this page Apr 22, 2014 · 9 revisions

Renderers follow the Factory Pattern. Please read about the factory pattern to learn about creation, declaration, and extending renderers.

See also: API Reference on renderers

Official Renderers

Creating a Custom Renderer

Creating a renderer is fairly easy since it follows the Factory Pattern. The general idea is:

  1. render the meta data (if enabled) to show FPS, etc
  2. Store information in the .view property of each body if it helps optimise the rendering process
  3. loop over the bodies in the world and draw a representation at the correct coordinates

There is a suggested pattern for creating the renderer which involves overriding the following methods of the base renderer: .init( options ), .createView( geometry, styles ), .drawMeta( meta ), .drawBody( body ).

.init( options )

Should call the parent init method: parent.init.call(this, options); so that options are properly set, and the rendering dom element is set as this.el. Further initialization can be done.

.createView( geometry, styles )

Should return a truthy value (likely an object) that can be referenced from body.view to facilitate rendering in some way. For example: The canvas renderer returns an image object that is effectively a sprite of the body, because drawing an image to canvas is faster than calling canvas drawing methods on every render.

.drawMeta( meta )

Should render the meta data in some way. For example, the DOM canvas renderer stores two SPAN tags in this.els.fps and this.els.ipf, and inserts the values of the meta information into those HTMLElements:

drawMeta: function( meta ){

    this.els.fps.innerHTML = meta.fps.toFixed(2);
    this.els.ipf.innerHTML = meta.ipf;
},

.drawBody( body )

Should render the body in some way. This will involve reading the body position and rotation. For example:

drawBody: function( body ){
    // "t" is the "leftover" time between timesteps. You can either ignore it, 
    // or use it to interpolate the position by multiplying it by the velocity 
    // and adding it to the position. This ensures smooth motion during "bullet-time"
    var t = this._interpolateTime;
    var view = body.view;
    var x = body.state.pos.get(0) + t * body.state.vel.get(0);
    var y = body.state.pos.get(1) + t * body.state.vel.get(1);
    var angle = body.state.angular.pos + t * body.state.angular.vel;

    // render "view" at (x, y) with a rotation of "angle"...
},

.beforeRender()

This is an additional (optional) method that can be added which will get automatically called just before rendering.

Further customization

If the suggested pattern for rendering is not adequate, you can also override the .render( bodies, meta ) method. NOTE: if you override the render method, the previous methods (.createView, .drawBody, .drawMeta, .beforeRender) will NOT get called unless you call them.

Ideally even if the .render method gets overridden, the following should still be used right before the rendering stage:

// inside .render()
this._world.emit('beforeRender', {
    renderer: this,
    bodies: bodies,
    meta: meta
});

if (this.options.meta){
    this.drawMeta( meta );
}
// render...