Jump to content

WebGL "invalid image" error


ForgeableSum
 Share

Recommended Posts

I keep getting WebGL "invalid image" error and I'm positive it's because of this:

http://www.lofibucket.com/articles/webgl_invalid_image.html

 

Basically the image hasn't finished loading yet and webgl is trying to render it. 

 

the images are actually created from bitmap data objects, manipulated, and then the base64s are extracted (from the BMP) and used as the src of a sprite texture atlas... so really, the images are not being loaded in the conventional way phaser loads images. but, this is all happening in the preload state, so I can't see why it would cause a problem for webgl. 

 

How do I "set a delay" so to speak on webgl or tell it to wait for those images before rendering? Or even just putting an arbitrary time delay on webgl should work. Any ideas?

Link to comment
Share on other sites

can you share your code or make a version on sandbox? i'm not sure if it'll all work just using preload state

Unfortunately that would be extremely difficult to do as the functions involved are so messy and have so much unrelated stuff .. All the assets of my game, including images, are served from Drupal. It's not a typical  html5 game setup, there is server-side code involved as well.

 

Anyway, I have figured out a glorious solution. Instead of recreating the images each time the game loads, recreate them once and store them on the server-side, likely as text in the database. That makes the most sense really. It will fix this issue and also result in much faster load times.  

Link to comment
Share on other sites

Okay well I spent all night working in that server-side mechanism and of course, it didn't work! I'm still seeing the strange black boxes in webgl. Here is my code (stripped down of all the nonsense). The image is already loaded into Phaser the normal way (not seen in this function) ... then I create a BMD, replace RGB, get the dataURL and finally swap the src of the image in cache (game.cache._images[uKey].data.src) with the new data URL. I think that's where the problem lies - it feels a bit hacky, so there's likely something I'm doing to disrupt Phaser/Pixi. Appreciate any help on this. 

 



function createColorVariants() {
 
    for (var q = 0; q < unitsAndBuildingsNodeObjects.length; q++) {
        var title = unitsAndBuildingsNodeObjects[q].title;
        var objectType = unitsAndBuildingsNodeObjects[q].object_type;
        for (var i = 0; i < playerColors.length; i++) {
 
            var uKey = getUniqueKey(unitsAndBuildingsNodeObjects[q].object_type, unitsAndBuildingsNodeObjects[q].nid, playerColors.player, unitsAndBuildingsNodeObjects[q].age);
 
           
                var imageTemplate = game.add.bitmapData();
                imageTemplate.load(unitsAndBuildingsNodeObjects[q].key + 'IMGTemplate');
 
 
            for (var r = 0; r < playerColors.rgbs.length; r++) {
                var values = playerColors.rgbs[r];
 
          
 
                    imageTemplate.replaceRGB(values[0][0], values[0][1], values[0][2], values[0][3], values[1][0], values[1][1], values[1][2], values[1][3]);
 
 
            }
 
 
                if (loadAtlasFromServer) {
 
                    if (gameAssetsKeyed[uKey].alt_texture_atlases != undefined) {
                        var string = gameAssetsKeyed[uKey].alt_texture_atlases;
                        gameAssetsKeyed[uKey].alt_texture_atlases = string.split('*');
                    }
console.log('loaded ' + uKey + ' from server'); 
                    game.cache._images[uKey].data.src = gameAssetsKeyed[uKey].alt_texture_atlases[playerColors.player - 2];
 
                } else {
                    var imageTemplateBase64 = imageTemplate.canvas.toDataURL('image/png', 1);
                    imageTemplateBase64.crossOrigin = '';
                    game.cache._images[uKey].data.src = imageTemplateBase64;
console.log('created ' + uKey + ' on the client side');
                }
 
 
 
        }
 
    }
 
}

 

 

Link to comment
Share on other sites

i just meant a snippet of code you are using to make and store an image. 

 

eg I've done this before http://www.html5gamedevs.com/topic/5683-add-bitmapdata-to-cache-as-image/?p=99067

btw what you did is a bit different from what I'm attempting to do. You added an image uri from a bmd into the cache. I want to not only add it to the cache, but load it as a texture atlas. I attempted to do this in a less hacky way, by loading it the normal way after it's been converted:

 

It's the same code as above, except replace:
 
game.cache._images[uKey].data.src = gameAssetsKeyed[uKey].alt_texture_atlases[playerColors.player - 2];
 

with:

game.load.atlasJSONHash(uKey, gameAssetsKeyed[uKey].alt_texture_atlases[playerColors.player - 2], gameAssetsKeyed[uKey].json);
 
gameAssetsKeyed[uKey].alt_texture_atlases[playerColors.player - 2] us the image URL string. Why won't it load? I knoe it's a perfectly valid image as I can open it up in another browser tab and it shows. 
Link to comment
Share on other sites

i think i was having a similar issue here

http://www.html5gamedevs.com/topic/18106-replace-image-atlas-in-cache-with-bitmapdata/

 

can you not have a prior game state that puts everything in the cache, and then load it in the next game state?

 

also I don't know if this is any use to you

http://phaser.io/docs/2.4.4/Phaser.BitmapData.html#generateTexture

generateTexture(key) → {PIXI.Texture}Creates a new Image element by converting this BitmapDatas canvas into a dataURL.The image is then stored in the image Cache using the key given. Finally a PIXI.Texture is created based on the image and returned.You can apply the texture to a sprite or any other supporting object by using either the key or the texture. First call generateTexture:var texture = bitmapdata.generateTexture('ball');Then you can either apply the texture to a sprite:game.add.sprite(0, 0, texture);or by using the string based key:game.add.sprite(0, 0, 'ball');
Link to comment
Share on other sites

No matter what I do, wether I use game.cache.addTextureAtlas or game.load.atlasJSONHash (god only know the difference), the results are the same. If I supply the image as a data URL (base64) instead of an actual url (http:example.com/example.png), sometimes it will load fine and sometimes it will not. The times it doesn't load, the sprite's pixi texture is messed up. It's "noFrame" attribute is set to true. Its texture in PIXI.textureCache has its valid property set to false (and therefore, is not rendering). I went through every line of code in Phaser where .valid is set to true or false on the texture ... but none of them are being set. I have no idea how/why the texture is not valid! I literally console.logged every instance of .valid set to true of false and could not find where it is being set to invalid. 

 

I can't understand why SOMETIMES it will load and SOMETIMES it will not. It makes absolutely no sense. 

 

2 days wasted. This is such a pain in the ass. 

Link to comment
Share on other sites

Here's an updated version of the functions. 

function getJSONObjectFromURL(url) {// note async is turned off    var framesObject;    $.ajax({        url: url,        dataType: 'json',        async: false,        success: function(object) {            framesObject = object;            return framesObject;        }    });    return framesObject;}function loadAtlasJSONHASHFromData(key, data, jsonURL) {    var image = new Image();    image.src = data;    image.crossOrigin = true;    var frameData = getJSONObjectFromURL(jsonURL);    game.cache.addTextureAtlas(key, null, image, frameData, Phaser.Loader.TEXTURE_ATLAS_JSON_HASH);    if (PIXI.TextureCache[key].noFrame == true) {        // noFrame is randomly true. When it's try, the texture does not work        console.log(PIXI.TextureCache[key]);    }}

 

I have no idea why PIXI.TextureCache[key].noFrame is sometimes true and sometimes false. When it's false, everything works fine. Every time you refresh the browser, it's like a coin is being tossed. Sometimes the frame data doesn't get associated with the texture -- but where/how is this association created? There is nothing in the documentation about it. 

Link to comment
Share on other sites

I've fixed it by doing waiting 1 second before executing game.cache.addTextureAtlas:

 

function loadAtlasJSONHASHFromData(key, data, jsonURL) {    var image = new Image();    image.src = data;    image.crossOrigin = true;    var frameData = getJSONObjectFromURL(jsonURL);setTimeout(function() {    game.cache.addTextureAtlas(key, null, image, frameData, Phaser.Loader.TEXTURE_ATLAS_JSON_HASH);},1000);     if (PIXI.TextureCache[key].noFrame == true) {        // noFrame is randomly true. When it's try, the texture does not work        console.log(PIXI.TextureCache[key]);    }}

No clue why it's necessary ... The ajax call us supposed to be non-asynchronous, yet it's acting like it's async. I'd appreciate any advice on this as this really does feel hacky and therefore, could be problematic in some software environments.  

 
Link to comment
Share on other sites

Okay, I figured out why it's necessary. 

 

The way I'm doing it:

I'm loading assets in preload. Essentially creating templates I can manipulate to create more images. These assets finish loading once the create state begins ALWAYS.

in create, I'm manipulating my images (with BMDs) and then trying to load additional images (the alternative player colors) based on those. this is problematic because ...

There are no assurances the images will finish loading by the time you add them to the game because they were not loaded in the preload state. 

 

You can't create a new image, by extracting the base64s from a BMD, for example, and use that image in the very same game state..

 

So what I thought of doing was creating 2 states: 1 for loading the image templates and another for manipulating (in preload) and adding to world (in create). The problem with this is that Phaser.Cache does not carry over from one state to another...I either need to get the cache to carry over from 1 state to another or I need to create a sort of intermediary state between preload and create that manipulates images from preload and loads those manipulate images into cache before create begins. 

 

How!?

Link to comment
Share on other sites

Are you saying that in the below code, the image loaded in the Boot state will be available in create of the Play state?

 

var BasicGame = function(game) {};BasicGame.Boot = function(game) {};BasicGame.Play = function(game) {};BasicGame.Boot.prototype = {    preload: function() {        game.load.image('assetA', URL);        game.state.start('Play');    }};BasicGame.Play.prototype = {    preload: function() {    },    create: function() {        game.add.image(0, 0, 'assetA');/// IS THIS IMAGE IN THE CACHE AND AVAILABLE?     },    update: function() {    },    render: function() {    }}game.state.add('Boot', BasicGame.Boot);game.state.add("Play", BasicGame.Play);game.state.start('Boot');

That has not been my experience. Perhaps there is something wrong somewhere else in my code? Why do I get the feeling I misunderstand something very fundamental about game states?

Link to comment
Share on other sites

Yes, what's wrong with your code is that you start the PlayState before the loading has even begun. Doing game.load.image('assetA', URL); does not actually load the asset, it puts it into the loading queue. The actual loading is triggered by Phaser once the preload() function of your state has completed, and the create() function will be called only once the loading is complete.

 

So what you should do is : 

BasicGame.Boot.prototype = {    preload: function() {        game.load.image('assetA', URL);      }    create: function(){        game.state.start('Play');    }};

See doc : http://phaser.io/docs/2.4.4/Phaser.State.html#preload

 

This is very well explained in Interphase 1! :)

Link to comment
Share on other sites

Yes, what's wrong with your code is that you start the PlayState before the loading has even begun. Doing game.load.image('assetA', URL); does not actually load the asset, it puts it into the loading queue. The actual loading is triggered by Phaser once the preload() function of your state has completed, and the create() function will be called only once the loading is complete.

 

So what you should do is : 

BasicGame.Boot.prototype = {    preload: function() {        game.load.image('assetA', URL);      }    create: function(){        game.state.start('Play');    }};

See doc : http://phaser.io/docs/2.4.4/Phaser.State.html#preload

 

This is very well explained in Interphase 1! :)

Thanks, this makes so much sense. I knew I was missing something fundamental. Now I am free to manipulate the hell out of my images because I can load them in 1 state and add them to the world in another state. 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...