soylomass

Drawing huge objects

Recommended Posts

Hi, I have a problem with all my games related to optimization, and it's about how to draw huge sprites that form background/terrain.

 

Examples:

1. deeeep.io

CwqCezkW8AAYs3r.jpg

 

This was my first one, it used Phaser, which used Pixi 2.x as renderer. It runs on canvas mode because in webgl mode it's much slower. In this game, the floor is created serverside as polygons, then the polygon points are sent to the client. In the client, I draw a squared tilesprite with the terrain design, then I write the polygon to a graphics object, and apply the graphics as a mask to the tilesprite.

 

2. raaaaft.io

Screenshot_18.thumb.png.8abe42ac7fbd9f6212c0fa9350a17182.png

 

This game is made using pure Pixi v4, it runs on webgl by default. Something similar happens here. The islands are circles, with a pattern filling, so I create a square tilesprite, then I create a graphics, draw a circle on it, and then apply it as a mask to the tilesprite. When drawing huge islands, it uses a lot of resorces.

 

--------------------

 

Is there a better way to do these? I'm sure there is, but I've never been able to think of one. I've tried for example drawing a small vesion of these and then scaling them, but both the patterns and the borders look blurred.

 

I'd be extremely thankful if anyone could help me think of a solution.

 

Thanks!

Share this post


Link to post
Share on other sites

you can split you huge BG sprites in multi textures.
Take a look to texturePacker

The maximum texture size for mobile devices should not exceed 2048. Bigger textures might not be displayed on some devices or might cause jittering sprites.

For pc version, don't worry about memory if your pc are not a old dinosaure.

And  for "canvas mode because in webgl mode it's much slower"

!!! hum no i don't think so , canva mode are very slow vs webGl thencologie.

My game, on my side, run at 12 fps in canvas mode vs 60fps in webGl!
If am not wrong , only cool feature for use canvas mode it for allow all blendmodes.
http://pixijs.download/dev/docs/PIXI.html#.BLEND_MODES

Also Pixi 2.x it obsolete and very old ! 
You will maybe have a lot of performance issue with this verison.
Recommande you to use last versions.

I can not give more help sorry, because i don't know the api Phaser.

 


 

Share this post


Link to post
Share on other sites
1 hour ago, jonforum said:

you can split you huge BG sprites in multi textures.
Take a look to texturePacker

The maximum texture size for mobile devices should not exceed 2048. Bigger textures might not be displayed on some devices or might cause jittering sprites.

For pc version, don't worry about memory if your pc are not a old dinosaure.

And  for "canvas mode because in webgl mode it's much slower"

!!! hum no i don't think so , canva mode are very slow vs webGl thencologie.

My game, on my side, run at 12 fps in canvas mode vs 60fps in webGl!
If am not wrong , only cool feature for use canvas mode it for allow all blendmodes.
http://pixijs.download/dev/docs/PIXI.html#.BLEND_MODES

Also Pixi 2.x it obsolete and very old ! 
You will maybe have a lot of performance issue with this verison.
Recommande you to use last versions.

I can not give more help sorry, because i don't know the api Phaser.

 


 

I changed the title because it may lead to misunderstandings. The sprites use small images, but they are repeated using big tilesprites.

Share this post


Link to post
Share on other sites

Hey,

Ivan made me aware of the question. he pushed me in the right direction and ill share the code. Masks are slow, its better to convert a graphic shape to a mesh and draw the shape using its vertices.

	

// In the case of my editor, somewhere i set the body.mySprite.data.tileTexture to the lookup name of the tileTexture, e.g. 'grass.jpg'

this.updateBodyTileSprite = function (body) {

    const tileTexture = body.mySprite.data.tileTexture;

    if (tileTexture && tileTexture != "") {
        let tex = PIXI.Texture.fromImage(tileTexture);
        tex.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
        if (!body.myTileSprite) {

            //body.originalGraphic is the shape we want to get the mesh from
            game.app.renderer.plugins.graphics.updateGraphics(body.originalGraphic);

            const verticesColor = body.originalGraphic._webGL[game.app.renderer.CONTEXT_UID].data[0].glPoints;
            let vertices = new Float32Array(verticesColor.length / 3);

            let i;
            let j = 0;
            for (i = 0; i < verticesColor.length; i += 6) {
                vertices[j] = verticesColor[i];
                vertices[j + 1] = verticesColor[i + 1];
                j += 2;
            }

            const indices = body.originalGraphic._webGL[game.app.renderer.CONTEXT_UID].data[0].glIndices;
            let uvs = new Float32Array(vertices.length);
            for (i = 0; i < vertices.length; i++) uvs[i] = vertices[i] * 2.0 / tex.width;

            const mesh = new PIXI.mesh.Mesh(tex, vertices, uvs, indices);
            body.mySprite.addChild(mesh);

            body.myTileSprite = mesh;
        }
        body.myTileSprite.texture = tex;

    } else if (body.myTileSprite) {
        body.myTileSprite.mask = null;
        body.myTileSprite.parent.removeChild(body.myTileSprite);
        body.myTileSprite = undefined;
    }
}

 

 

Share this post


Link to post
Share on other sites
4 hours ago, unrealnl said:

Hey,

Ivan made me aware of the question. he pushed me in the right direction and ill share the code. Masks are slow, its better to convert a graphic shape to a mesh and draw the shape using its vertices.

...

Wow! Thanks for sharing this! You gave me a good reason to migrate Deeeep to pure Pixi, as phaser 2 doesn't have Mesh.

Share this post


Link to post
Share on other sites

Hi, could it be that a mesh with a repeating texture only works in WebGL mode? In canvas the meshes are invisible, unless I configure the uvs so as they draw a stretched texture, in that case it works on both Canvas and WebGL.

In all cases baseTexture.wrapMode is set as PIXI.WRAP_MODES.REPEAT, I only change de uvs values:

I've triangulated all the polygons so the meshes are triangles, drawn as

let mesh = new PIXI.mesh.Mesh(tex, vertices, indices, DRAW_MODES.TRIANGLES) (with and without the last argument)

0 and 1 uvss in Canvas:

Screenshot_34.png.e0b3a5acac815a058857012b43b1c50e.png

 

0 and 1 uvss in WebGL:

Screenshot_35.png.5d040218dfefd0098d100427b3c35cfe.png

 

Bigger uvs in WebGL:

Screenshot_36.png.e89b79f6820dd86094f10050675ed1a1.png

 

Bigger uvs in Canvas:

Meshes aren't visible, but no error shown.

Share this post


Link to post
Share on other sites
59 minutes ago, ivan.popelyshev said:

Yep, that hack is only for webgl. If you need canvas, you can enhance pixi graphics CanvasRenderer so it accepts a texture and creates a CanvasPattern like TilingSprite does.

When I thought all my problems were solved 😴

I don't think that's at the reach of my capabilities, but what part of CanvasRenderer should I look at? CanvasMeshRenderer?

Share this post


Link to post
Share on other sites

No, you should look how TilingSprite works and move that behaviour on Graphics, add a secret 'texture" and use it to create pattern :) Pixi is not friendly to canvas2d but the architecture is good enough to put there better implementations. Good Luck with it!

Share this post


Link to post
Share on other sites
3 hours ago, ivan.popelyshev said:

No, you should look how TilingSprite works and move that behaviour on Graphics, add a secret 'texture" and use it to create pattern :) Pixi is not friendly to canvas2d but the architecture is good enough to put there better implementations. Good Luck with it!

I've achieved a result using the method used by @george here: http://www.html5gamedevs.com/topic/15223-piximesh-textured-polygon-from-box2d-a-question-of-triangles-uvs/?tab=comments#comment-86335

Basically, editing MeshSpriteRenderer.prototype._renderDrawTriangle and replacing the drawImage at the end with the canvasPatter from tilingsprite.

Screenshot_38.png.b9153217e8d0ba2bbbb1c06106cbae3e.png

 

It looks pretty good, but the performance is awfull, and degrades over time until the game crashes (without drawing anything else). I know that culling will help with this, but is there any kind of optimization I could add to this?

 

Thanks in advance

 

EDIT: I just noticed that that draw function is constantly called, that's why the fps degrade over time. I'll implement some state storing the pattern on the mesh, and will try to reutilise the canvas pattern to see if it improves performance.

Share this post


Link to post
Share on other sites

The above solution with the constant canvasPattern creation fixed, storing it in the mesh object, works at 60fps, but with frequent fps drops that cause micro-stuttering. I'll try to see what else I can do to improve the performance.

When I'm done, I'll post the result here.

Share this post


Link to post
Share on other sites

Update:

Reutilising canvas patterns that share the same texture, finally I got constant 60fps! Drawing all the hundreds of triangles that form the whole map! Thats a 30x improvement from using tilesprites and masks in the way I was using them in Phaser 😃

Thanks to all of you for your support, now I'll continue with rewriting the Deeeep's client.

PD: The solution was to edit lib/mesh/canvas/CanvasMeshRenderer.js's MeshSpriteRenderer.prototype._renderDrawTriangle function, replacing:

context.drawImage(
            textureSource,
            0,
            0,
            textureWidth * base.resolution,
            textureHeight * base.resolution,
            0,
            0,
            textureWidth,
            textureHeight
        );

with:

// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
var imageUrl = texture.baseTexture.imageUrl;
if(!_canvasPattern[imageUrl])
{
  var baseTexture = texture.baseTexture;
  const baseTextureResolution = baseTexture.resolution;

  // cut an object from a spritesheet..
  const tempCanvas = new core.CanvasRenderTarget(texture._frame.width,
	texture._frame.height,
	baseTextureResolution);

  // Tint the tiling sprite
  /*if (this.tint !== 0xFFFFFF)
  {
	var tintedTexture = CanvasTinter.getTintedTexture(mesh, mesh.tint);
	tempCanvas.context.drawImage(tintedTexture, 0, 0);
  }
  else
  {
	tempCanvas.context.drawImage(baseTexture.source,
	  -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution);
  }
  this.cachedTint = this.tint;*/
  tempCanvas.context.drawImage(baseTexture.source,
	-texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution);
  _canvasPattern[imageUrl] = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat');
}
context.fillStyle = _canvasPattern[imageUrl];
context.fill();

 

As you see, I'm storing the canvasPatterns in a variable on that file, I'll try to make that cleaner. Also, I've commented the tint section because I'm not using tint right now.

 

Now, the only thing that's left is figuring out why in Canvas there's a separation between all triangles, while in WebGL there isn't, but this isn't that important.

Screenshot_39.png.ea58cd3fb42c9d8f9b5185a85b914b76.png

(note: not talking about the big separation on the left, but the micro separations between all triangles, look at the center)

 

Thanks again for all your help!

Share this post


Link to post
Share on other sites

you dont have to use Mesh for canvas, Graphics will be faster because its mapped into natural vector format (moveTo,lineTo), add a texture to graphics and use the pattern inside GraphicsCanvasRenderer. May be that'll be faster because it doesnt need extra "cull" calls.

In v5 Graphics will support both webgl and canvas texture fills.

EDIT:

or you can use moveTo/lineTo in your extension of Mesh class, instead of culling. Better to make new class that extends Mesh than changing Mesh itself - that can be made into v4 plugin!

Share this post


Link to post
Share on other sites
17 minutes ago, ivan.popelyshev said:

you dont have to use Mesh for canvas, Graphics will be faster because its mapped into natural vector format (moveTo,lineTo), add a texture to graphics and use the pattern inside GraphicsCanvasRenderer. May be that'll be faster because it doesnt need extra "cull" calls.

In v5 Graphics will support both webgl and canvas texture fills.

EDIT:

or you can use moveTo/lineTo in your extension of Mesh class, instead of culling. Better to make new class that extends Mesh than changing Mesh itself - that can be made into v4 plugin!

Hi, I'll try one of those solutions once I get back home.

About Pixi 5, will the migration be straightforward? (That it will be easier than migrating from phaser 2 to Pixi 4 Is out of doubt 😄)

Share this post


Link to post
Share on other sites
On 7/26/2018 at 3:47 PM, ivan.popelyshev said:

the internals of Mesh and Graphics are different. Yep, you can build your own phaser on pixi-v4, and, when the time comes, migrate to v5.

I have a question, if I want to create a simple object like a parallelogram with a plain color background, what's the most performant way? Using a Graphics object with drawPolygon, or using a Mesh? Considering both WebGL and Canvas.

I've looked for the performance differences between Graphic and Mesh but didn't find an answer.

Thanks

Share this post


Link to post
Share on other sites
2 hours ago, ivan.popelyshev said:

i guess both will work the same way. parallelogram can be just a sprite with skew transform and tinted color, "PIXI.Texture.WHITE" that way you can batch them , if you need it, of course :)

Wow, just find out there are those kind of transforms. Wish I had read more before starting to code games. Thanks again for your extremely helpful support!

Share this post


Link to post
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...

  • Recently Browsing   0 members

    No registered users viewing this page.