i changed my way of loading the normal in PIXI v4 with spriteIlluminator and texturePackers.
This seems to work , did you already think about including that (natively) for the v5?
I do not know if I proceeded in the right way because I had a lot of difficulty in debugging the spritesheetParser.
If there are suggestions on something cleaner I am a taker. :)
it allow to access to a normal texture like this
PIXI.Sprite(sheet.textures["image"] diff
PIXI.Sprite(sheet.textures_n["image"] normal
exports.default = function () {
return function spritesheetParser(resource, next) {
var imageResourceName = resource.name + '_image';
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
if (!resource.data || resource.type !== _resourceLoader.Resource.TYPE.JSON || !resource.data.frames || this.resources[imageResourceName]) {
next();
return;
}
var loadOptions = {
crossOrigin: resource.crossOrigin,
metadata: resource.metadata.imageMetadata,
parentResource: resource
};
var resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res) {
if (res.error) {
next(res.error);
return;
}
var spritesheet = new _core.Spritesheet(res.texture.baseTexture, resource.data, resource.url);
spritesheet.parse(function () {
resource.spritesheet = spritesheet;
resource.textures = spritesheet.textures;
next();
});
});
///******************* LOAD NORMAL
if(resource.data.meta.normal_map){
// load the image normals for this sheet
this.add(imageResourceName+"_n", resourcePath.replace('.png','_n.png'), loadOptions, function onImageLoad(res) {
if (res.error) {
next(res.error);
return;
};
var spritesheet = new _core.Spritesheet(res.texture.baseTexture, resource.data, resource.url);
spritesheet.parse(function () {
resource.textures_n = Object.assign({},spritesheet.textures);
//resource.spritesheet.baseTexture_n = Object.assign({},spritesheet.baseTexture);
},{normal:'_n'}); // pass option normal
});
};
};
// add options normal
Spritesheet.prototype.parse = function parse(callback,option={}) {
this._batchIndex = 0;
this._callback = callback;
if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) {
this._processFrames(0,option);
this._processAnimations();
this._parseComplete();
} else {
this._nextBatch();
}
};
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
_.Texture.addToCache(this.textures[i], i+(option.normal||''));




I'm pretty sure this can be a separate middleware that runs after the spritesheet parser, rather than putting it into the spritesheet parser in pixi's core.
did you have example, my old way look like this , and it too mutch code for nothing.
I wish I could do without this, permanently so that pixi manages himself this kind of process with texturePacker
Normal and multi-pack animation work but i don't like how i proceed.
It should be done in ts in pixjs?:(
/** start load scenes stuff */
loadScene(SCENE){
const dataSheets = this.isValidJson(this.DATA.scene[SCENE]);
if(!dataSheets){ return this.load() }; // break and continue next scene
const loader = new PIXI.loaders.Loader();
dataSheets.forEach(fileName => {
if(fileName.contains('-')){ // is multipack
this.LINKS[fileName.split('-')[0]].forEach(pack => {
loader.add(pack.name, pack.path);
});
}else{ loader.add(fileName, this.LINKS[fileName]) }
});
loader.load();
loader.onProgress.add((loader, res) => {
!['json','webm'].contains(res.extension) && delete loader.resources[res.name];
this.txtLog = res.url;
});
loader.onComplete.add((loader, res) => { this.loadNormals(SCENE,res) });
!loader.loading && loader.onComplete._tail._fn(); // force continue if nothing to load;
};
/** load les png normal si besoin */
loadNormals(SCENE,RES_D) {
const loader = new PIXI.loaders.Loader();
Object.values(RES_D).filter((res)=>res.extension === 'json').forEach(res => {
if(res.data.meta && res.data.meta.normal_map){
loader.add(res.name, res.url.replace('.json','_n.png'));
};
});
loader.load();
loader.onProgress.add((loader, res) => { this.txtLog = res.url });
loader.onComplete.add((loader, res) => { this.loadEnd(SCENE,RES_D,res) });
!loader.loading && loader.onComplete._tail._fn();
};
/** computing result */
loadEnd(SCENE,RES_D,RES_N) {
const data2 = this.data2;
this.createNormals(RES_D,RES_N);
this.combineMultiPack(RES_D,RES_N);
Object.values(RES_D).forEach(res_d => {
const name = res_d.name.contains('-0')? res_d.name.split('-')[0] : res_d.name;
data2[name] = new _dataBase(SCENE,res_d,RES_N && RES_N[res_d.name]);
SCENE === 'Scene_Boot' && Object.defineProperty( data2, name, {enumerable: false}); // protect data from destroy
});
this.load();
};
thand create textures
createNormals(RES_D,RES_N){
if(!RES_N){return};
// .clone(); ? check if we need clone or just link ?
for (let i=0, k=Object.keys(RES_D), l=k.length; i<l; i++) {
const res_d = RES_D[k[i]];
const res_n = RES_N[k[i]];
if(!res_n){ continue };
const textures_n = {};
for (const key in res_d.textures) {
const tex = res_d.textures[key];
const frame = tex.frame;
const orig = tex.orig;
const trim = tex.trim;
const rot = res_d.data.frames[key].rotated ? 2 : 0; // base.data.rotated ? 2 : 0 ?
const n = textures_n[key] = new PIXI.Texture(res_n.texture.baseTexture, frame, orig, trim, rot, res_d.data.frames[key].anchor);
n.textureCacheIds = [key+'_n'];
};
res_n.textures = textures_n;
};
};
I give you a concrete example with your bunny mascot.
it's an all in one scenario.
Animation + multi-pack + normals
In theory, we should only had to load the file bunnyAniSheets-0.json
And PIXI should automatically compute and load the multi-pack, animations and normals attached in the json. meta.
https://www.dropbox.com/s/hxuec99g2j98r3s/bunnyPack.zip
"animations": {
"bunny": ["bunny_00069","bunny_00070","bunny_00071","bunny_00073","bunny_00074","bunny_00075","bunny_00076","bunny_00077","bunny_00078","bunny_00079","bunny_00080","bunny_00081","bunny_00082","bunny_00083","bunny_00084","bunny_00085"]
},
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "bunnyAniSheets-0.png",
"format": "RGBA8888",
"size": {"w":488,"h":488},
"scale": "1",
"normal_map": "bunnyAniSheets-0_n.png",
"related_multi_packs": [ "bunnyAniSheets-1.json", "bunnyAniSheets-2.json", "bunnyAniSheets-3.json", "bunnyAniSheets-4.json", "bunnyAniSheets-5.json" ],
"smartupdate": "$TexturePacker:SmartUpdate:8f6e33909450e6d322971ce1e0f96e75:ad7c7a02c4cc9f091c958b4b3d663332:dddbeb199181f6df386015d3e1348488$"
}
@CodeAndWeb
I just meant the part you added to the parser middleware could just be a separate middleware in your own code rather than changing the one in pixi's library.
For example:
loader.use(function spritesheetNormalsParser(resource, next) {
var imageResourceName = resource.name + '_image_n';
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
if (!resource.data || resource.type !== _resourceLoader.Resource.TYPE.JSON || !resource.data.frames || this.resources[imageResourceName]) {
next();
return;
}
var loadOptions = {
crossOrigin: resource.crossOrigin,
metadata: resource.metadata.imageMetadata,
parentResource: resource
};
var resourcePath = getResourcePath(resource, this.baseUrl).replace('.png','_n.png');
if(resource.data.meta.normal_map){
// load the image normals for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res) {
if (res.error) {
next(res.error);
return;
};
var spritesheet = new _core.Spritesheet(res.texture.baseTexture, resource.data, resource.url);
spritesheet.parse(function () {
resource.textures_n = Object.assign({},spritesheet.textures);
//resource.spritesheet.baseTexture_n = Object.assign({},spritesheet.baseTexture);
},{normal:'_n'}); // pass option normal
});
};
});
And now you have a normals parser middleware in your own code without changes to pixi itself.
ho thank you so much for the tips , this will help me
We'll look into adding it in one of plugins later.
hey thanks ivan this will be cool
grrr ok after hours to debug about loader.pre and loader.use

Many problems with scope and dynamic multi-packs and normal parentResource constraint....
const loader = new PIXI.loaders.Loader();
loader.use(function spritesheetNormalsParser(resource, next) {
const sourceName = resource.name.split('_image')[0];
const parentJSON = this.resources[sourceName];
const normalSuffix = parentJSON.data.meta.normal_map.split(parentJSON.name)[1].split('.png')[0];
const imageResourceName = sourceName+"_image"+normalSuffix;
if(parentJSON.data.meta.normal_map && !this.resources[imageResourceName] ){
//const normalSuffix = parentJson.data.meta.normal_map.split(parentJson.name)[1].split('.png')[0];
const resourcePath = resource.url.replace(parentJSON.data.meta.image,parentJSON.data.meta.normal_map);
const loadOptions = {
crossOrigin: resource.crossOrigin,
metadata: resource.metadata.imageMetadata,
parentResource: resource
};
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res) {
if (res.error) { return next(res.error) };
var spritesheet = new PIXI.Spritesheet(res.texture.baseTexture, resource.data, res.url);
spritesheet.parse(function () {
resource.spritesheet = spritesheet;
resource.textures = spritesheet.textures;
next();
});
});
}else{
return next();
}
});
At least i have tried, but I will rather return back to my old ways that uses 3 loaders and combines resources once completed.
thanks anyway for the time for help.