Han

Hello everyone,

I'm a beginner who is using spine-webgl, and is cloning multiple spine animations like below :
var mySpineObjA_Children1 = new spine.Skeleton(skeletonA.data);
var mySpineObjA_Children2 = new spine.Skeleton(skeletonA.data);
For animation playing, it works fine in my project which has a few spine animations using the same skeleton data.

The problem is, I can not clear the view of a specific skeleton without affecting other skeletons.

I've searched for clearing methods and here's what I got :
gl.clear(mask)
which to be called once after a specific pair of skeleton and animation state has been removed from update().
But it causes an one-frame-disappear to other spine animations which has to be played as usual.
I thought it might because of gl.clear() clears actually all pixels drew by this gl renderer.

So I wonder if there's a normal way to remove a specific spine animation from my stage in a project using spine-webgl.

Thanks.
Han
Posts: 6

badlogic

There's a difference between how the HTML DOM works and how WebGL works. With the DOM, if you remove an element, the DOM renderer will redraw everything else that's still in the DOM. In WebGL, if you call gl.clear(), the drawing surface (canvas) will be erased, and it is your responsibility to redraw the entire scene with the skeletons and other graphics you want to be displayed.

Usually with WebGL, you use requestAnimationFrame() to schedule a rendering operation every few milliseconds (depending on screen refresh rate and other factors), in which you draw the entire scene as you want it in that one frame. That rendering operation usually starts by clearing the canvas via gl.clear(), then proceeds to redraw everything you want on screen.
User avatar
badlogic

Mario
Posts: 979

Han

badlogic wrote:There's a difference between how the HTML DOM works and how WebGL works. With the DOM, if you remove an element, the DOM renderer will redraw everything else that's still in the DOM. In WebGL, if you call gl.clear(), the drawing surface (canvas) will be erased, and it is your responsibility to redraw the entire scene with the skeletons and other graphics you want to be displayed.

Usually with WebGL, you use requestAnimationFrame() to schedule a rendering operation every few milliseconds (depending on screen refresh rate and other factors), in which you draw the entire scene as you want it in that one frame. That rendering operation usually starts by clearing the canvas via gl.clear(), then proceeds to redraw everything you want on screen.
Thanks for responding.

I put all skeletons in one canvas and drew them like :
update() {
for(let i = 0; i < mySkeletons.length; i++) {
// do something to mySkeletons[i].skeleton...
// do something to mySkeletons[i].state...
}
}
This canvas is on the top of my project, which is based on Phaser, and covers all Phaser sprites.

So, is it recommended to create a new gl renderer each skeleton and let them call requestAnimationFrame() on their own?
Han
Posts: 6

badlogic

I'm afraid I don't see any rendering code there. If you are using Phase, your best bet is to try the 3rd party Spine Phaser runtime https://github.com/orange-games/phaser-spine That should resolve all your problems.
User avatar
badlogic

Mario
Posts: 979

Han

badlogic wrote:I'm afraid I don't see any rendering code there. If you are using Phase, your best bet is to try the 3rd party Spine Phaser runtime https://github.com/orange-games/phaser-spine That should resolve all your problems.
I'm sorry for providing incomplete code.
update() {
const now = Date.now() / 1000;
for (let i = 0; i < this.skeletonsForRender.length; i++) {
if(!this.skeletonsForRender[i]) { return; }
const state = this.skeletonsForRender[i].state;
const skeleton = this.skeletonsForRender[i].skeleton;

let delta;
delta = now - this.lastFrameTimes[i];
this.lastFrameTimes[i] = now;

state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
this.shader.bind();
this.shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
this.shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, this.mvp.values);
this.batcher.begin(this.shader);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.batcher.end();
this.shader.unbind();
}
}
I render all skeletons in a for() loop, and it displays weird when calling gl.clear() after I removed any of them from the array.

Of course I've already tried phaser-spine but so far it doesn't support mesh, that's why I use spine-webgl instead.
It seemed there's still many problems with spine-webgl, I can't even find a way provided by the official to clear just one specified animation on my screen.
Anyway, thanks for replying.
Han
Posts: 6

Nate

As badlogic explained, spine-webgl (and all game toolkits) work by rapidly rendering the whole screen, usually 60 times per second. It is not possible to just "clear just one specified animation" from your screen. If you have a skeleton on your screen, it is because you are drawing it every time the screen is rendered (60 times per second). If you have been drawing the skeleton for a while and then want it to disappear, then you need to stop drawing it on subsequent screen renders. By this I mean to stop calling skeletonRenderer.draw.

It sounds like you have a web page where you are putting a WebGL canvas on top of Phaser's rendering. Is that right? In that case, maybe what you want is to remove the WebGL canvas entirely. You can do that like this, where canvasID is the ID of your canvas DOM node:
document.getElementById("canvasID").remove();
If this still doesn't help, I suggest showing an example of what you are doing so we can better understand the problem.
User avatar
Nate

Nate
Posts: 7422

Han

Nate wrote:It sounds like you have a web page where you are putting a WebGL canvas on top of Phaser's rendering. Is that right? In that case, maybe what you want is to remove the WebGL canvas entirely. You can do that like this, where canvasID is the ID of your canvas DOM node:
document.getElementById("canvasID").remove();
If this still doesn't help, I suggest showing an example of what you are doing so we can better understand the problem.
Right, and I can understand that is an effective way.
But remove the WebGL canvas would remove all my skeletons, and having multiple WebGL canvas in my project is not allowed.
So in the beginning, I do such a stupid way like state.update(undefined); to implement clearing a specified skeleton.

Here's my simplified code:
removeSkeleton(targetIndex) {
mySkeletons[targetIndex] = null;
drawEmpty(targetIndex);
}

drawEmpty(targetIndex) {
myStates[targetIndex].update(undefined);
}

update() {
const now = Date.now() / 1000;
for (let i = 0; i < this.mySkeletons.length; i++) {

if(!this.mySkeletons[i]) {
continue;
}

const state = this.myStates[i];
const skeleton = this.mySkeletons[i].skeleton;

let delta;
delta = now - this.lastFrameTimes[i];
this.lastFrameTimes[i] = now;

state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
this.shader.bind();
this.shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
this.shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, this.mvp.values);
this.batcher.begin(this.shader);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.batcher.end();
this.shader.unbind();
}
}
This definitely TROLL but somehow it works for me, clearing the target skeleton on my screen.

I was expecting there's something or method that I can do with state, skeleton or something it's own property to achieve clearing.
Now I think it's clear that the best way to use Spine-WebGL is creating canvas DOM for each Spine animation, am I right?
Then, replanning this project may be my following job.
Han
Posts: 6

Nate

Han wrote:Now I think it's clear that the best way to use Spine-WebGL is creating canvas DOM for each Spine animation, am I right?
No, that is not a good idea. Your descriptions of your problem make it sound like that is what you are doing.

It's very simple: you are calling skeletonRenderer.draw to draw your skeleton. If you want to stop drawing your skeleton, stop calling skeletonRenderer.draw!
User avatar
Nate

Nate
Posts: 7422

Han

Nate wrote:It's very simple: you are calling skeletonRenderer.draw to draw your skeleton. If you want to stop drawing your skeleton, stop calling skeletonRenderer.draw!
That's exactly what I did.
I removed specified skeleton out of mySkeletons array, which for() looping and calling skeletonRenderer.draw in update().
Though that specified skeleton has stopped, it left a stopping-animation, just like a graphic, on my canvas.

Stop calling skeletonRenderer.draw makes it STOP but not CLEAR at all, that's what Spine-WebGL deficient in.
Han
Posts: 6

Nate

There is no such deficiency in spine-webgl -- you are in complete control of rendering. You should be drawing the whole screen 60 times per second. At the start of each screen draw, you should be clearing the screen using gl.clear, then drawing your skeletons and other images. If you no longer draw the skeleton, it will disappear the next time the screen is drawn.

Maybe you aren't calling gl.clear at the start of each screen render? I can't say because you haven't posted a self contained example showing the problem you are having. It is frustrating that we have to guess at what your actual problem is. Help us help you, it will be much more efficient usage of both of our time.

Here's some spine-webgl code which calls gl.clear:
spine-runtimes/transitions.js at 3.6
That code in action can be seen here:
http://rawgit.com/EsotericSoftware/spine-runtimes/3.6/spine-ts/webgl/demos/transitions.html
Don't call gl.clear in the middle of rendering the screen, since that would erase everything you've rendered so far.

It might help to take a look at the spine-webgl demos:
spine-runtimes/README.md at 3.6
User avatar
Nate

Nate
Posts: 7422

Han

Nate wrote:Maybe you aren't calling gl.clear at the start of each screen render? I can't say because you haven't posted a self contained example showing the problem you are having. It is frustrating that we have to guess at what your actual problem is. Help us help you, it will be much more efficient usage of both of our time.
It's true.
Calling gl.clear before skeletonRenderer.draw solves all my problems.
I was calling it mistakenly, though examples has already shown the right way.

Somehow I ignored
gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
gl.clear(gl.COLOR_BUFFER_BIT);
in render() and call gl.clear manually.

I am sorry about my language above.

Now I can figure it out, finally.
badlogic and Nate, both thanks.
Han
Posts: 6

Nate

Woo! :party: No worries, I'm glad you got it worked out. :)
User avatar
Nate

Nate
Posts: 7422


Return to Runtimes