Jump to content

Sprite2D sets the size to (0,0) by default


AlbertTJames
 Share

Recommended Posts

Hey everyone,

 

So I have been fighting Sprite2D for hours. I am developping a 2D version of my project and I simply wanted to draw a single sprite2D on my canvas but couldn't, the sprite was not showing. The texture was loading for sure because I could use it on 3D meshes. Another post talked about a similar issue and although the post says the problem was solved, it does not say how or what was really causing it : 

 

 I am not showing all my experimenting because it would be meaningless, but I kept getting this warning:

(index):1 [.Offscreen-For-WebGL-0x7f84b30bdc00]RENDER WARNING: there is no texture bound to the unit 0

and all the posts on the web about those kind of errors were very foggy... hours passed, thought it was the server, the loading, the transparency... without having any kind of improvement, so I started to cut everything from my code util I was only creating an engine and basically copied paste the babylon example http://babylonjs-playground.com/#20MSFF#16

By th way I got also this very benign warning caused by the BABYLON.Texture.NEAREST_SAMPLINGMODE :

[.Offscreen-For-WebGL-0x7f84b30bdc00]RENDER WARNING: there is no texture bound to the unit 0
babylon.max.js:7027 'CanvasRenderingContext2D.webkitImageSmoothingEnabled' is deprecated. Please use 'CanvasRenderingContext2D.imageSmoothingEnabled' instead.

 

this warning comes from this line _this._workingContext.webkitImageSmoothingEnabled = false; on 7027 babylon.max.js. But this warning has nothing to do with the sprite2D not showing.

 

To cut a very long story short :

When settings = {} passed to new BABYLON.Sprite2D do not contain a spriteSize property, the sprite.size SHOULD be set to the actual texture size, but it is in fact set to size(0,0) and hence the sprite is not displayed.

This behavior does not seem to be on purpose because the docs clearly state the sprite would take the size of the texture. The code also point in that direction (42630 babylon.max.js):

function Sprite2D(texture, settings) {
            if (!settings) {
                settings = {};
            }
            _super.call(this, settings);
            this.texture = texture;
            this.texture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
            this.texture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
            this.size = settings.spriteSize || null; // LOOK HERE !
            this.spriteLocation = settings.spriteLocation || new BABYLON.Vector2(0, 0);
            this.spriteFrame = 0;
            this.invertY = (settings.invertY == null) ? false : settings.invertY;
            this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
            this._isTransparent = true;
            if (!this.size) { /// AND HERE !!
                var s = texture.getSize();
                this.size = new BABYLON.Size(s.width, s.height);
            }
        }

So I tried to understand why this.size was not set to the texture size...

So first of all, when the super is called the size is set to (0,0).

Then it SHOULD be set to null if spriteSize is not set. But there is an interaction between getters and setters.

Since size is a SmartPropertyPrim... the GETTER is called everytime you call the setter on 36968:

SmartPropertyPrim._hookProperty = function (propId, piStore, typeLevelCompare, dirtyBoundingInfo, kind) {
            return function (target, propName, descriptor) {
                var propInfo = SmartPropertyPrim._createPropInfo(target, propName, propId, dirtyBoundingInfo, typeLevelCompare, kind);
                if (piStore) {
                    piStore(propInfo);
                }
                var getter = descriptor.get, setter = descriptor.set;
                // Overload the property setter implementation to add our own logic
                descriptor.set = function (val) {
                    // check for disposed first, do nothing
                    if (this.isDisposed) {
                        return;
                    }
                    var curVal = getter.call(this);
                    if (SmartPropertyPrim._checkUnchanged(curVal, val)) {
                        return;
                    }
                    // Cast the object we're working one
                    var prim = this;
                    // Change the value
                    setter.call(this, val);
                    // Notify change, dirty flags update
                    prim._handlePropChanged(curVal, val, propName, propInfo, typeLevelCompare);
                };
            };
        };

The getter is :

Object.defineProperty(Prim2DBase.prototype, "size", {
            /**
             * Size of the primitive or its bounding area
             * BEWARE: if you change only size.width or height it won't trigger a property change and you won't have the expected behavior.
             * Use this property to set a new Size object, otherwise to change only the width/height use Prim2DBase.width or height properties.
             */
            get: function () {
                if (!this._size || this._size.width == null || this._size.height == null) {
                    if (Prim2DBase.boundinbBoxReentrency) {
                        return Prim2DBase.nullSize;
                    }
                    if (this._boundingSize) {
                        return this._boundingSize;
                    }
                    Prim2DBase.boundinbBoxReentrency = true;
                    var b = this.boundingInfo;
                    Prim2DBase.boundinbBoxReentrency = false;
                    return this._boundingSize;
                }
                return this._size;
            },
...

So basically a size (0,0) is returned if size is null sooo this:

if (!this.size) { /// AND HERE !!

Will never be true.

 

I think.

 

Hope it helps - time to sleep ! :)

Link to comment
Share on other sites

So it seems that has been adressed already, in src/Canvas2d/babylon.sprite2d.ts (357)  :

 if (settings.spriteSize == null || !texture.isReady()) {
                if (texture.isReady()) {
                    this.size = <Size>texture.getBaseSize();
                } else {
                    texture.onLoadObservable.add(() => {
                        if (settings.spriteSize == null) {
                            this.size = <Size>texture.getBaseSize();
                        }
                        this._positioningDirty();
                        this._instanceDirtyFlags |= Prim2DBase.originProperty.flagId | Sprite2D.textureProperty.flagId;  // To make sure the sprite is issued again for render
                    });
                }

 

What is the best way to use this version ? Do I have to compile it ? i have never used ts really..

 

EDIT: ok nvm. https://github.com/BabylonJS/Babylon.js/tree/master/Tools/Gulp

 

EDIT2: So indeed now the texture size correctly used.

Link to comment
Share on other sites

Although the size is working correctly... texture do not have alpha anymore -_-

 

 

Screen Shot 2016-09-24 at 15.29.09.png

 

Tried everything. No idea how to fix it - tired... 

 

Ok i really do have to give a project on Wednesday using sprites so lets try to find the problem:

From what i gather the render mode is decided here:

RenderablePrim2D.prototype._updateRenderMode = function () {
            if (this.isTransparent) {
                this._renderMode = BABYLON.Render2DContext.RenderModeTransparent;
            }
            else if (this.isAlphaTest) {
                this._renderMode = BABYLON.Render2DContext.RenderModeAlphaTest;
            }
            else {
                this._renderMode = BABYLON.Render2DContext.RenderModeOpaque;
            }
        };

 

renderablePrim2d.ts 361: when this isTransparent getter is called for the sprite it returns false. The problem might be that the sprite is created in a scene that is not shown...  The first time the function is called the texture is undefined 

Object.defineProperty(RenderablePrim2D.prototype, "isTransparent", {
            get: function () {
                return (this.actualOpacity < 1) || this._shouldUseAlphaFromTexture() || this._isPrimTransparent();
            },
            enumerable: true,
            configurable: true
        });

On line 485 of babylon.sprite2D.ts :

Sprite2D.prototype._shouldUseAlphaFromTexture = function () {
            return this.texture != null && this.texture.hasAlpha && this.useAlphaFromTexture;
        };

This is called with an undefined texture. 

 

Althought the texture was passed to the sprite2D. This is because the super is called before the texture is set here :

 function Sprite2D(texture, settings) {
            var _this = this;
            if (!settings) {
                settings = {};
            }
            _super.call(this, settings);
            
            this.texture = texture;

So when this._shouldUseAlphaFromTexture() is called no texture is set, this calls the sprite2D._shouldUseAlphaFromTexture() functions with an undefined texture and this.texture != null returns false since its not a hard compare !==

 sprite2d.texture.hasAlpha is false.

Then in sprite2D constructor useAlphaFromTexture is set to true

Object.defineProperty(Sprite2D.prototype, "useAlphaFromTexture", {
            get: function () {
                return this._useAlphaFromTexture;
            },
            set: function (value) {
                if (this._useAlphaFromTexture === value) {
                    return;
                }
                this._useAlphaFromTexture = value;
                this._updateRenderMode();
            },
            enumerable: true,
            configurable: true
        });

And this._updateRenderMode() is called. And here the magic should happen. updateRendeMode calls isTransparent and isTransparent calls should use alpha from texture. But this time texture is not undefined, it was set in the sprite2D constructor just before.

Still, is transparent is not true, so this becomes clearer to everyone of course

Screen Shot 2016-09-24 at 16.30.18.png

this.texture has no alpha. Make sense. It did in the previous version but not this one.

 

 

SO of course now it all makes sense and if I had texture.hasAlpha before calling the sprite2D constructor... it works :

var texture = new BABYLON.Texture(taskObject.ASSETS_FOLDER + "/sprites/adventurer/player.png", scene);

texture.hasAlpha = true; // That...

 var sprite = new BABYLON.Sprite2D(texture, {
   ...

Screen Shot 2016-09-24 at 16.41.17.png

 

After all... what is 3 hours of life, in the grand scheme of things. I will go buy myself a big pizza to congratulate myself for my lack of neurons.

 

Link to comment
Share on other sites

Just to summarize :

I First part

Problem: In the current distribution of babylonJS the sprite2D size is not set to the texture size if you do not specify a size in the settings, resulting in a size(0,0) and no render

Solution: this issue has been corrected in the current typescript source files.

So I forked the repo, cloned it, $sudo npm install -g gulp, cd REPO, cd /Tools/Gulp, $npm install, $gulp.

This generates a new babylon.max.js in the dist/preview release folder of your local repo. Use this file.

 

II Second Part

Problem: The texture has a good size now Yayyyy! But... Somehow the texture is opaque now... it is not in the previous version of babylon... but it is in this one O.o ... wtf

Solution: Just set the texture.hasAlpha = true.

 

I find solace in the hope people are as dumb as me and will need such advices. :unsure:.... :D 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

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