lettuce
Version:
Lettuce JS, Mini Mobile Framework for Romantic with DSL.
340 lines (321 loc) • 13 kB
JavaScript
/*
* MelonJS Game Engine
* Copyright (C) 2011 - 2013, Olivier BIOT
* http://www.melonjs.org
*
*/
(function () {
/**
* a local constant for the -(Math.PI / 2) value
* @ignore
*/
var nhPI = -(Math.PI / 2);
/**
* A Texture atlas object<br>
* Currently support : <br>
* - [TexturePacker]{@link http://www.codeandweb.com/texturepacker/} : through JSON export <br>
* - [ShoeBox]{@link http://renderhjs.net/shoebox/} : through JSON export using the
* melonJS setting [file]{@link https://github.com/melonjs/melonJS/raw/master/media/shoebox_JSON_export.sbx} <br>
* - Standard (fixed cell size) spritesheet : through a {framewidth:xx, frameheight:xx} object
* @class
* @extends Object
* @memberOf me.video.renderer
* @name Texture
* @constructor
* @param {Object} atlas atlas information. See {@link me.loader#getJSON}
* @param {Image} [texture=atlas.meta.image] texture name
* @example
* // create a texture atlas from a JSON Object
* texture = new me.video.renderer.Texture(
* me.loader.getJSON("texture"),
* me.loader.getImage("texture")
* );
*
* // create a texture atlas for a spritesheet
* texture = new me.video.renderer.Texture(
* {framewidth:32, frameheight:32},
* me.loader.getImage("spritesheet")
* );
*/
me.CanvasRenderer.prototype.Texture = Object.extend(
/** @scope me.video.renderer.Texture.prototype */
{
/**
* @ignore
*/
init : function (atlas, texture) {
/**
* to identify the atlas format (e.g. texture packer)
* @ignore
*/
this.format = null;
/**
* the image texture itself
* @ignore
*/
this.texture = texture || null;
/**
* the atlas dictionnary
* @ignore
*/
this.atlas = null;
if (typeof (atlas) !== "undefined") {
if (typeof(atlas.meta) !== "undefined") {
// Texture Packer
if (atlas.meta.app.contains("texturepacker")) {
this.format = "texturepacker";
// set the texture
if (typeof(texture) === "undefined") {
var name = me.utils.getBasename(atlas.meta.image);
this.texture = me.loader.getImage(name);
if (this.texture === null) {
throw new me.video.renderer.Texture.Error("Atlas texture '" + name + "' not found");
}
} else {
this.texture = texture;
}
}
// ShoeBox
else if (atlas.meta.app.contains("ShoeBox")) {
if (!atlas.meta.exporter || !atlas.meta.exporter.contains("melonJS")) {
throw new me.video.renderer.Texture.Error(
"ShoeBox requires the JSON exporter : " +
"https://github.com/melonjs/melonJS/tree/master/media/shoebox_JSON_export.sbx"
);
}
this.format = "ShoeBox";
// set the texture
this.texture = texture;
}
// Internal texture atlas
else if (atlas.meta.app.contains("melonJS")) {
this.format = "melonJS";
this.texture = texture;
}
// initialize the atlas
this.atlas = this.build(atlas);
} else {
// a regular spritesheet ?
if (typeof(atlas.framewidth) !== "undefined" &&
typeof(atlas.frameheight) !== "undefined") {
this.format = "Spritesheet (fixed cell size)";
if (typeof(texture) !== undefined) {
// overwrite if specified
atlas.image = texture;
}
// initialize the atlas
this.atlas = this.buildFromSpriteSheet(atlas);
}
}
}
// if format not recognized
if (!this.atlas) {
throw new me.video.renderer.Texture.Error("texture atlas format not supported");
}
},
/**
* @ignore
*/
build : function (data) {
var atlas = {};
data.frames.forEach(function (frame) {
// fix wrongly formatted JSON (e.g. last dummy object in ShoeBox)
if (frame.hasOwnProperty("filename")) {
// Source coordinates
var s = frame.frame;
atlas[frame.filename] = {
name : name, // frame name
offset : new me.Vector2d(s.x, s.y),
width : s.w,
height : s.h,
angle : (frame.rotated === true) ? nhPI : 0
};
}
});
return atlas;
},
/**
* @ignore
* build an atlas from the given spritesheet
*/
buildFromSpriteSheet : function (data) {
var atlas = []; // changing to {} breaks everything !
var image = data.region || data.image;
var spacing = data.spacing || 0;
var margin = data.margin || 0;
// calculate the sprite count (line, col)
if ((image.width - margin) % (data.framewidth + spacing) !== 0 ||
(image.height - margin) % (data.frameheight + spacing) !== 0) {
throw new me.video.renderer.Texture.Error(
"Spritesheet Texture for image: " + image.src +
" is not divisible by " + (data.framewidth + spacing) +
"x" + (data.frameheight + spacing)
);
}
var spritecount = new me.Vector2d(
~~((image.width - margin) / (data.framewidth + spacing)),
~~((image.height - margin) / (data.frameheight + spacing))
);
var offsetX = 0;
var offsetY = 0;
if (image.offset) {
offsetX = image.offset.x;
offsetY = image.offset.y;
}
// build the local atlas
for (var frame = 0, count = spritecount.x * spritecount.y; frame < count ; frame++) {
atlas[frame] = {
name: "" + frame,
offset: new me.Vector2d(
margin + (spacing + data.framewidth) * (frame % spritecount.x) + offsetX,
margin + (spacing + data.frameheight) * ~~(frame / spritecount.x) + offsetY
),
width: data.framewidth,
height: data.frameheight,
angle: 0
};
}
return atlas;
},
/**
* return the Atlas dictionnary
* @name getAtlas
* @memberOf me.video.renderer.Texture
* @function
* @return {Object}
*/
getAtlas : function () {
return this.atlas;
},
/**
* return the Atlas texture
* @name getTexture
* @memberOf me.video.renderer.Texture
* @function
* @return {Image}
*/
getTexture : function () {
return this.texture;
},
/**
* return a normalized region/frame information for the specified sprite name
* @name getRegion
* @memberOf me.video.renderer.Texture
* @function
* @param {String} name name of the sprite
* @return {Object}
*/
getRegion : function (name) {
return this.atlas[name];
},
/**
* Create a sprite object using the first region found using the specified name
* @name createSpriteFromName
* @memberOf me.video.renderer.Texture
* @function
* @param {String} name name of the sprite
* @return {me.Sprite}
* @example
* // create a new texture atlas object under the `game` namespace
* game.texture = new me.video.renderer.Texture(
* me.loader.getJSON("texture"),
* me.loader.getImage("texture")
* );
* ...
* ...
* // add the coin sprite as renderable for the entity
* this.renderable = game.texture.createSpriteFromName("coin.png");
* // set the renderable position to bottom center
* this.anchorPoint.set(0.5, 1.0);
*/
createSpriteFromName : function (name) {
var region = this.getRegion(name);
if (region) {
// instantiate a new sprite object
var sprite = new me.Sprite(
0, 0,
this.getTexture(),
region.width, region.height
);
// set the sprite offset within the texture
sprite.offset.setV(region.offset);
// set angle if defined
sprite._sourceAngle = region.angle;
// return our object
return sprite;
}
// throw an error
throw new me.video.renderer.Texture.Error("Texture - region for " + name + " not found");
},
/**
* Create an animation object using the first region found using all specified names
* @name createAnimationFromName
* @memberOf me.video.renderer.Texture
* @function
* @param {String[]||Number[]} names list of names for each sprite
* (when manually creating a Texture out of a spritesheet, only numeric values are authorized)
* @return {me.AnimationSheet}
* @example
* // create a new texture atlas object under the `game` namespace
* game.texture = new me.video.renderer.Texture(
* me.loader.getJSON("texture"),
* me.loader.getImage("texture")
* );
* ...
* ...
* // create a new animationSheet as renderable for the entity
* this.renderable = game.texture.createAnimationFromName([
* "walk0001.png", "walk0002.png", "walk0003.png",
* "walk0004.png", "walk0005.png", "walk0006.png",
* "walk0007.png", "walk0008.png", "walk0009.png",
* "walk0010.png", "walk0011.png"
* ]);
*
* // define an additional basic walking animation
* this.renderable.addAnimation ("simple_walk", [0,2,1]);
* // you can also use frame name to define your animation
* this.renderable.addAnimation ("speed_walk", ["walk0007.png", "walk0008.png", "walk0009.png", "walk0010.png"]);
* // set the default animation
* this.renderable.setCurrentAnimation("simple_walk");
* // set the renderable position to bottom center
* this.anchorPoint.set(0.5, 1.0);
*/
createAnimationFromName : function (names) {
var tpAtlas = [], indices = {};
// iterate through the given names
// and create a "normalized" atlas
for (var i = 0; i < names.length;++i) {
tpAtlas[i] = this.getRegion(names[i]);
indices[names[i]] = i;
if (tpAtlas[i] == null) {
// throw an error
throw new me.video.renderer.Texture.Error("Texture - region for " + names[i] + " not found");
}
}
// instantiate a new animation sheet object
return new me.AnimationSheet(0, 0, {
image: this.texture,
framewidth: 0,
frameheight: 0,
margin: 0,
spacing: 0,
atlas: tpAtlas,
atlasIndices: indices
});
}
});
/**
* Base class for Texture exception handling.
* @name Error
* @class
* @memberOf me.video.renderer.Texture
* @constructor
* @param {String} msg Error message.
*/
me.CanvasRenderer.prototype.Texture.Error = me.Error.extend({
init : function (msg) {
this._super(me.Error, "init", [ msg ]);
this.name = "me.CanvasRenderer.Texture.Error";
}
});
})();