diff --git a/src/module/Outro.js b/src/module/Outro.js index f855f3f..71a0207 100644 --- a/src/module/Outro.js +++ b/src/module/Outro.js @@ -31,6 +31,7 @@ Matter.Vector = Vector; Matter.Vertices = Vertices; Matter.Gui = Gui; Matter.Render = Render; +Matter.RenderPixi = RenderPixi; Matter.Events = Events; // CommonJS module diff --git a/src/render/RenderPixi.js b/src/render/RenderPixi.js new file mode 100644 index 0000000..5faba68 --- /dev/null +++ b/src/render/RenderPixi.js @@ -0,0 +1,365 @@ +/** +* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) +* and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples. +* +* @class RenderPixi +*/ + +var RenderPixi = {}; + +(function() { + + /** + * Creates a new Pixi.js WebGL renderer + * @method create + * @param {object} options + * @return {RenderPixi} A new renderer + */ + RenderPixi.create = function(options) { + var defaults = { + controller: RenderPixi, + element: null, + canvas: null, + options: { + width: 800, + height: 600, + background: '#fafafa', + wireframeBackground: '#222', + enabled: true, + wireframes: true, + showSleeping: true, + showDebug: false, + showBroadphase: false, + showBounds: false, + showVelocity: false, + showCollisions: false, + showAxes: false, + showPositions: false, + showAngleIndicator: false, + showIds: false, + showShadows: false + } + }; + + var render = Common.extend(defaults, options); + + // init pixi + render.context = new PIXI.WebGLRenderer(800, 600, render.canvas, false, true); + render.canvas = render.context.view; + render.stage = new PIXI.Stage(); + + // caches + render.textures = {}; + render.sprites = {}; + render.primitives = {}; + + // use a sprite batch for performance + render.spriteBatch = new PIXI.SpriteBatch(); + render.stage.addChild(render.spriteBatch); + + // insert canvas + if (Common.isElement(render.element)) { + render.element.appendChild(render.canvas); + } else { + Common.log('No "render.element" passed, "render.canvas" was not inserted into document.', 'warn'); + } + + return render; + }; + + /** + * Clears the scene graph + * @method clear + * @param {RenderPixi} render + */ + RenderPixi.clear = function(render) { + var stage = render.stage, + spriteBatch = render.spriteBatch; + + // clear stage + while (stage.children[0]) { + stage.removeChild(stage.children[0]); + } + + // clear sprite batch + while (spriteBatch.children[0]) { + spriteBatch.removeChild(spriteBatch.children[0]); + } + + var bgSprite = render.sprites['bg-0']; + + // clear caches + render.textures = {}; + render.sprites = {}; + render.primitives = {}; + + // set background sprite + render.sprites['bg-0'] = bgSprite; + if (bgSprite) + spriteBatch.addChildAt(bgSprite, 0); + + // add sprite batch back into stage + render.stage.addChild(render.spriteBatch); + + // reset background state + render.currentBackground = null; + }; + + /** + * Sets the background of the canvas + * @method setBackground + * @param {RenderPixi} render + * @param {string} background + */ + RenderPixi.setBackground = function(render, background) { + if (render.currentBackground !== background) { + var isColor = background.indexOf && background.indexOf('#') !== -1, + bgSprite = render.sprites['bg-0']; + + if (isColor) { + // if solid background color + var color = Common.colorToNumber(background); + render.stage.setBackgroundColor(color); + + // remove background sprite if existing + if (bgSprite) + render.spriteBatch.removeChild(bgSprite); + } else { + // initialise background sprite if needed + if (!bgSprite) { + var texture = _getTexture(render, background); + + bgSprite = render.sprites['bg-0'] = new PIXI.Sprite(texture); + bgSprite.position.x = 0; + bgSprite.position.y = 0; + render.spriteBatch.addChildAt(bgSprite, 0); + } + } + + render.currentBackground = background; + } + }; + + /** + * Description + * @method world + * @param {engine} engine + */ + RenderPixi.world = function(engine) { + var render = engine.render, + world = engine.world, + context = render.context, + stage = render.stage, + options = render.options, + i; + + if (options.wireframes) { + RenderPixi.setBackground(render, options.wireframeBackground); + } else { + RenderPixi.setBackground(render, options.background); + } + + for (i = 0; i < world.bodies.length; i++) + RenderPixi.body(engine, world.bodies[i]); + + for (i = 0; i < world.constraints.length; i++) + RenderPixi.constraint(engine, world.constraints[i]); + + context.render(stage); + }; + + + /** + * Description + * @method constraint + * @param {engine} engine + * @param {constraint} constraint + */ + RenderPixi.constraint = function(engine, constraint) { + var render = engine.render, + bodyA = constraint.bodyA, + bodyB = constraint.bodyB, + pointA = constraint.pointA, + pointB = constraint.pointB, + stage = render.stage, + constraintRender = constraint.render, + primitiveId = 'c-' + constraint.id, + primitive = render.primitives[primitiveId]; + + // initialise constraint primitive if not existing + if (!primitive) + primitive = render.primitives[primitiveId] = new PIXI.Graphics(); + + // don't render if constraint does not have two end points + if (!constraintRender.visible || !constraint.pointA || !constraint.pointB) { + primitive.clear(); + return; + } + + // add to scene graph if not already there + if (stage.children.indexOf(primitive) === -1) + stage.addChild(primitive); + + // render the constraint on every update, since they can change dynamically + primitive.clear(); + primitive.beginFill(0, 0); + primitive.lineStyle(constraintRender.lineWidth, Common.colorToNumber(constraintRender.strokeStyle), 1); + + if (bodyA) { + primitive.moveTo(bodyA.position.x + pointA.x, bodyA.position.y + pointA.y); + } else { + primitive.moveTo(pointA.x, pointA.y); + } + + if (bodyB) { + primitive.lineTo(bodyB.position.x + pointB.x, bodyB.position.y + pointB.y); + } else { + primitive.lineTo(pointB.x, pointB.y); + } + + primitive.endFill(); + }; + + /** + * Description + * @method body + * @param {engine} engine + * @param {body} body + */ + RenderPixi.body = function(engine, body) { + var render = engine.render, + bodyRender = body.render; + + if (!bodyRender.visible) + return; + + if (bodyRender.sprite) { + var spriteId = 'b-' + body.id, + sprite = render.sprites[spriteId], + spriteBatch = render.spriteBatch; + + // initialise body sprite if not existing + if (!sprite) + sprite = render.sprites[spriteId] = _createBodySprite(render, body); + + // add to scene graph if not already there + if (spriteBatch.children.indexOf(sprite) === -1) + spriteBatch.addChild(sprite); + + // update body sprite + sprite.position.x = body.position.x; + sprite.position.y = body.position.y; + sprite.rotation = body.angle; + } else { + var primitiveId = 'b-' + body.id, + primitive = render.primitives[primitiveId], + stage = render.stage; + + // initialise body primitive if not existing + if (!primitive) { + primitive = render.primitives[primitiveId] = _createBodyPrimitive(render, body); + primitive.initialAngle = body.angle; + } + + // add to scene graph if not already there + if (stage.children.indexOf(primitive) === -1) + stage.addChild(primitive); + + // update body primitive + primitive.position.x = body.position.x; + primitive.position.y = body.position.y; + primitive.rotation = body.angle - primitive.initialAngle; + } + }; + + /** + * Creates a body sprite + * @method _createBodySprite + * @private + * @param {RenderPixi} render + * @param {body} body + * @return {PIXI.Sprite} sprite + */ + var _createBodySprite = function(render, body) { + var bodyRender = body.render, + texturePath = bodyRender.sprite.texture, + texture = _getTexture(render, texturePath), + sprite = new PIXI.Sprite(texture); + + sprite.anchor.x = 0.5; + sprite.anchor.y = 0.5; + + return sprite; + }; + + /** + * Creates a body primitive + * @method _createBodyPrimitive + * @private + * @param {RenderPixi} render + * @param {body} body + * @return {PIXI.Graphics} graphics + */ + var _createBodyPrimitive = function(render, body) { + var bodyRender = body.render, + options = render.options, + primitive = new PIXI.Graphics(); + + primitive.clear(); + + if (!options.wireframes) { + primitive.beginFill(Common.colorToNumber(bodyRender.fillStyle), 1); + primitive.lineStyle(body.render.lineWidth, Common.colorToNumber(bodyRender.strokeStyle), 1); + } else { + primitive.beginFill(0, 0); + primitive.lineStyle(1, Common.colorToNumber('#bbb'), 1); + } + + primitive.moveTo(body.vertices[0].x - body.position.x, body.vertices[0].y - body.position.y); + + for (var j = 1; j < body.vertices.length; j++) { + primitive.lineTo(body.vertices[j].x - body.position.x, body.vertices[j].y - body.position.y); + } + + primitive.lineTo(body.vertices[0].x - body.position.x, body.vertices[0].y - body.position.y); + + primitive.endFill(); + + // angle indicator + if (options.showAngleIndicator || options.showAxes) { + primitive.beginFill(0, 0); + + if (options.wireframes) { + primitive.lineStyle(1, Common.colorToNumber('#CD5C5C'), 1); + } else { + primitive.lineStyle(1, Common.colorToNumber(body.render.strokeStyle)); + } + + primitive.moveTo(0, 0); + primitive.lineTo(((body.vertices[0].x + body.vertices[body.vertices.length-1].x) / 2) - body.position.x, + ((body.vertices[0].y + body.vertices[body.vertices.length-1].y) / 2) - body.position.y); + + primitive.endFill(); + } + + return primitive; + }; + + /** + * Gets the requested texture (a PIXI.Texture) via its path + * @method _getTexture + * @private + * @param {RenderPixi} render + * @param {string} imagePath + * @return {PIXI.Texture} texture + */ + var _getTexture = function(render, imagePath) { + var texture = render.textures[imagePath]; + + if (!texture) + texture = render.textures[imagePath] = PIXI.Texture.fromImage(imagePath); + + return texture; + }; + +})(); \ No newline at end of file