AlbertTJames Posted September 24, 2016 Share Posted September 24, 2016 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 ! Quote Link to comment Share on other sites More sharing options...
AlbertTJames Posted September 24, 2016 Author Share Posted September 24, 2016 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. Quote Link to comment Share on other sites More sharing options...
AlbertTJames Posted September 24, 2016 Author Share Posted September 24, 2016 Although the size is working correctly... texture do not have alpha anymore 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 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, { ... 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. Quote Link to comment Share on other sites More sharing options...
AlbertTJames Posted September 24, 2016 Author Share Posted September 24, 2016 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. .... Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted September 26, 2016 Share Posted September 26, 2016 Excellent! Thank you very much for sharing your findings Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.