/** * The `Matter.RenderPixi` module is an example renderer using pixi.js. * See also `Matter.Render` for a canvas based renderer. * * @class RenderPixi * @deprecated the Matter.RenderPixi module will soon be removed from the Matter.js core. * It will likely be moved to its own repository (but maintenance will be limited). */ var RenderPixi = {}; module.exports = RenderPixi; var Bounds = require('../geometry/Bounds'); var Composite = require('../body/Composite'); var Common = require('../core/Common'); var Events = require('../core/Events'); var Vector = require('../geometry/Vector'); (function() { var _requestAnimationFrame, _cancelAnimationFrame; if (typeof window !== 'undefined') { _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); }; _cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; } /** * Creates a new Pixi.js WebGL renderer * @method create * @param {object} options * @return {RenderPixi} A new renderer * @deprecated */ RenderPixi.create = function(options) { Common.warn('RenderPixi.create: Matter.RenderPixi is deprecated (see docs)'); var defaults = { controller: RenderPixi, engine: null, element: null, frameRequestId: null, canvas: null, renderer: null, container: null, spriteContainer: null, pixiOptions: null, options: { width: 800, height: 600, background: '#fafafa', wireframeBackground: '#222', hasBounds: false, 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), transparent = !render.options.wireframes && render.options.background === 'transparent'; // init pixi render.pixiOptions = render.pixiOptions || { view: render.canvas, transparent: transparent, antialias: true, backgroundColor: options.background }; render.mouse = options.mouse; render.engine = options.engine; render.renderer = render.renderer || new PIXI.WebGLRenderer(render.options.width, render.options.height, render.pixiOptions); render.container = render.container || new PIXI.Container(); render.spriteContainer = render.spriteContainer || new PIXI.Container(); render.canvas = render.canvas || render.renderer.view; render.bounds = render.bounds || { min: { x: 0, y: 0 }, max: { x: render.options.width, y: render.options.height } }; // event listeners Events.on(render.engine, 'beforeUpdate', function() { RenderPixi.clear(render); }); // caches render.textures = {}; render.sprites = {}; render.primitives = {}; // use a sprite batch for performance render.container.addChild(render.spriteContainer); // insert canvas if (Common.isElement(render.element)) { render.element.appendChild(render.canvas); } else { Common.warn('No "render.element" passed, "render.canvas" was not inserted into document.'); } // prevent menus on canvas render.canvas.oncontextmenu = function() { return false; }; render.canvas.onselectstart = function() { return false; }; return render; }; /** * Continuously updates the render canvas on the `requestAnimationFrame` event. * @method run * @param {render} render * @deprecated */ RenderPixi.run = function(render) { (function loop(time){ render.frameRequestId = _requestAnimationFrame(loop); RenderPixi.world(render); })(); }; /** * Ends execution of `Render.run` on the given `render`, by canceling the animation frame request event loop. * @method stop * @param {render} render * @deprecated */ RenderPixi.stop = function(render) { _cancelAnimationFrame(render.frameRequestId); }; /** * Clears the scene graph * @method clear * @param {RenderPixi} render * @deprecated */ RenderPixi.clear = function(render) { var container = render.container, spriteContainer = render.spriteContainer; // clear stage container while (container.children[0]) { container.removeChild(container.children[0]); } // clear sprite batch while (spriteContainer.children[0]) { spriteContainer.removeChild(spriteContainer.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) container.addChildAt(bgSprite, 0); // add sprite batch back into container render.container.addChild(render.spriteContainer); // reset background state render.currentBackground = null; // reset bounds transforms container.scale.set(1, 1); container.position.set(0, 0); }; /** * Sets the background of the canvas * @method setBackground * @param {RenderPixi} render * @param {string} background * @deprecated */ 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.renderer.backgroundColor = color; // remove background sprite if existing if (bgSprite) render.container.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.container.addChildAt(bgSprite, 0); } } render.currentBackground = background; } }; /** * Description * @method world * @param {engine} engine * @deprecated */ RenderPixi.world = function(render) { var engine = render.engine, world = engine.world, renderer = render.renderer, container = render.container, options = render.options, bodies = Composite.allBodies(world), allConstraints = Composite.allConstraints(world), constraints = [], i; if (options.wireframes) { RenderPixi.setBackground(render, options.wireframeBackground); } else { RenderPixi.setBackground(render, options.background); } // handle bounds var boundsWidth = render.bounds.max.x - render.bounds.min.x, boundsHeight = render.bounds.max.y - render.bounds.min.y, boundsScaleX = boundsWidth / render.options.width, boundsScaleY = boundsHeight / render.options.height; if (options.hasBounds) { // Hide bodies that are not in view for (i = 0; i < bodies.length; i++) { var body = bodies[i]; body.render.sprite.visible = Bounds.overlaps(body.bounds, render.bounds); } // filter out constraints that are not in view for (i = 0; i < allConstraints.length; i++) { var constraint = allConstraints[i], bodyA = constraint.bodyA, bodyB = constraint.bodyB, pointAWorld = constraint.pointA, pointBWorld = constraint.pointB; if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA); if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB); if (!pointAWorld || !pointBWorld) continue; if (Bounds.contains(render.bounds, pointAWorld) || Bounds.contains(render.bounds, pointBWorld)) constraints.push(constraint); } // transform the view container.scale.set(1 / boundsScaleX, 1 / boundsScaleY); container.position.set(-render.bounds.min.x * (1 / boundsScaleX), -render.bounds.min.y * (1 / boundsScaleY)); } else { constraints = allConstraints; } for (i = 0; i < bodies.length; i++) RenderPixi.body(render, bodies[i]); for (i = 0; i < constraints.length; i++) RenderPixi.constraint(render, constraints[i]); renderer.render(container); }; /** * Description * @method constraint * @param {engine} engine * @param {constraint} constraint * @deprecated */ RenderPixi.constraint = function(render, constraint) { var engine = render.engine, bodyA = constraint.bodyA, bodyB = constraint.bodyB, pointA = constraint.pointA, pointB = constraint.pointB, container = render.container, 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 (Common.indexOf(container.children, primitive) === -1) container.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 * @deprecated */ RenderPixi.body = function(render, body) { var engine = render.engine, bodyRender = body.render; if (!bodyRender.visible) return; if (bodyRender.sprite && bodyRender.sprite.texture) { var spriteId = 'b-' + body.id, sprite = render.sprites[spriteId], spriteContainer = render.spriteContainer; // initialise body sprite if not existing if (!sprite) sprite = render.sprites[spriteId] = _createBodySprite(render, body); // add to scene graph if not already there if (Common.indexOf(spriteContainer.children, sprite) === -1) spriteContainer.addChild(sprite); // update body sprite sprite.position.x = body.position.x; sprite.position.y = body.position.y; sprite.rotation = body.angle; sprite.scale.x = bodyRender.sprite.xScale || 1; sprite.scale.y = bodyRender.sprite.yScale || 1; } else { var primitiveId = 'b-' + body.id, primitive = render.primitives[primitiveId], container = render.container; // 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 (Common.indexOf(container.children, primitive) === -1) container.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 * @deprecated */ 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 = body.render.sprite.xOffset; sprite.anchor.y = body.render.sprite.yOffset; return sprite; }; /** * Creates a body primitive * @method _createBodyPrimitive * @private * @param {RenderPixi} render * @param {body} body * @return {PIXI.Graphics} graphics * @deprecated */ var _createBodyPrimitive = function(render, body) { var bodyRender = body.render, options = render.options, primitive = new PIXI.Graphics(), fillStyle = Common.colorToNumber(bodyRender.fillStyle), strokeStyle = Common.colorToNumber(bodyRender.strokeStyle), strokeStyleIndicator = Common.colorToNumber(bodyRender.strokeStyle), strokeStyleWireframe = Common.colorToNumber('#bbb'), strokeStyleWireframeIndicator = Common.colorToNumber('#CD5C5C'), part; primitive.clear(); // handle compound parts for (var k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) { part = body.parts[k]; if (!options.wireframes) { primitive.beginFill(fillStyle, 1); primitive.lineStyle(bodyRender.lineWidth, strokeStyle, 1); } else { primitive.beginFill(0, 0); primitive.lineStyle(1, strokeStyleWireframe, 1); } primitive.moveTo(part.vertices[0].x - body.position.x, part.vertices[0].y - body.position.y); for (var j = 1; j < part.vertices.length; j++) { primitive.lineTo(part.vertices[j].x - body.position.x, part.vertices[j].y - body.position.y); } primitive.lineTo(part.vertices[0].x - body.position.x, part.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, strokeStyleWireframeIndicator, 1); } else { primitive.lineStyle(1, strokeStyleIndicator); } primitive.moveTo(part.position.x - body.position.x, part.position.y - body.position.y); primitive.lineTo(((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2 - body.position.x), ((part.vertices[0].y + part.vertices[part.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 * @deprecated */ var _getTexture = function(render, imagePath) { var texture = render.textures[imagePath]; if (!texture) texture = render.textures[imagePath] = PIXI.Texture.fromImage(imagePath); return texture; }; })();