mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.5k stars 35.37k forks source link

CanvasRenderer Spritesheets with Rotation #1601

Closed eldernos closed 12 years ago

eldernos commented 12 years ago

Hello,

This is more of a support question than anything wrong with the engine. What I'm looking for is the best way to create a rather large tiled area with multiple layers for a 2.5D game. Originally, I used particles with assigned textures and that was mostly effective except that when I'd rotate the camera the sprites would remain locked on their axis, which was quite strange.

Now I'm trying to use a large mesh that uses an offscreen canvas as a texture. Then I only update the mesh texture when something changes to the underlying data. The performance is pretty solid except when I redraw to the texture, even just a few pixels of difference, it really bogs down the system. These textures are large (100 x 100 tiles each 32 x 32px).

The demo of this can be seen here: http://hurricanejs.zombienationonline.net/Frontend/Demo/Demo3/techdemo.html

To see the behavior, click on the green box when the scene renders and then right click anywhere else on the map to instruct it to move.

Depending on the browser I'm using, it either blinks and uses an insane amount of memory (I'm assuming from overlapping instances of the texture?) or, on my other machine, just bogs the render function down to perhaps a bit more than once per second. I profiled the functions in Chrome and found the main functions are the PatternPath (Vc) function and its call to the context.createPattern function. This leads me to believe I'm using the wrong object for rendering this because I think I'd rather not go through creating a pattern when all I need to do is draw a texture from memory.

I assume I'm doing this all wrong but if the Mesh is the wrong object, what is the correct one? Is there any way to update only a portion of an active texture or is it always all or nothing?

Any advice would be greatly appreciated!

mrdoob commented 12 years ago

The demo of this can be seen here: http://hurricanejs.zombienationonline.net/Frontend/Demo/Demo3/techdemo.html

To see the behavior, click on the green box when the scene renders and then right click anywhere else on the map to instruct it to move.

Doesn't seem to work here :/

I see the green square, like 5x5 pixels box but when I click on it it doesn't do anything... ?

eldernos commented 12 years ago

Hmmm... I'm getting it to work in Firefox 11 and Chrome 17.096. IE doesn't appear to accept the click for some reason to do with jQuery I'm assuming.

eldernos commented 12 years ago

I even just got it to work in IE9 but I had to step through the code for some reason for it to properly respond to the click event. Bizarre. Does it throw any errors in the console?

mrdoob commented 12 years ago

http://twitpic.com/92ksid/full

That's what I get.

eldernos commented 12 years ago

Well, that's the load screen and it looks right. I guess when you left click the selected item doesn't change? Bummer. I can't think of any reason the input wouldn't be registering.

I guess, if you can't get it to work, I just wonder if the plane geometry would be the most efficient way to build a tiled map? Would ~thousands of small tiles be more efficient than rather large textures? I would try to troubleshoot your problem with the code more but if there aren't any javascript errors I wouldn't know how to start.

Here is a screen of the problem...

http://twitpic.com/92mwfy

the code is rendering really fast (and in previous experiments where I rotate/zoom/pan) but when it starts to move it takes almost a second per render... over 90% of the processing is in the Vc function where it creates the pattern. Is there some other renderable object I can use that will accept texture updates more rapidly or do I need to go back to the drawing board?

mrdoob commented 12 years ago

To be honest, I don't think CanvasRenderer is a good option for doing 2D games.

I tried, and it wasn't nice: http://dl.dropbox.com/u/7508542/three.js/mario/index.html

I need to complain to the W3C about Canvas someday...

eldernos commented 12 years ago

Well, would it be more effective to use a WebGL? How much is the specification going to change in the near term? I'd like to build toward something that won't require constant rewriting.

I had mostly handled the basic problems with the incredible slowness of the canvas draw by layering and offscreen buffering but the approach didn't seem to work for modifying the items.

If this works for you (http://hurricanejs.zombienationonline.net/Frontend/Demo/Demo2/techdemo.html) you can see really snappy zooming/panning/rotation with the arrow keys, page up, page down, plus and minus.

I thought I was almost there but I needed some way to modify a small portion of the texture without retiling the whole mesh which doesn't appear to be possible. I'd like to build something that is truly cross-browser and it seems that IE will never support WebGL but if it isn't possible to get performance out of the canvas I may just have to go back to the drawing board with webgl.

Underneath the hood (and I've investigated but you may be able to just tell me) is there a way technically to just rewrite some of the texture on a mesh in the canvas? Perhaps I could extend the texture, material, or mesh class or something of that nature? I really like where I got in that second tech demo before attempting to have the screen update textures.

Also, to be clear, I think 3 might just be the greatest javascript thing I've ever seen, so I'm definitely not complaining.

mrdoob commented 12 years ago

Well, at least with WebGL you'll be able to avoid the diagonal line :)

Anyway, I still don't know what problem you're having as the first demo didn't work here.

mrdoob commented 12 years ago

Ah wait, I managed to make it work, I missed the "right click" part. Now I need to re-read the whole thread.

mrdoob commented 12 years ago

Ah, I see the problem now.

So the issue is that, because CanvasRenderer now fills triangles with pattern and the only way to update the texture is by creating a new pattern, big textures are a problem.

So what the green square is doing is being drawn in the texture.

Bummer.

Maybe I should revert and use clipped images again with some kind of flag for using patterns when needed (repeated textures).

eldernos commented 12 years ago

I think that either adding a flag on Texture would be great or adding a PatternTexture/Texture distinction. I guess also some type of CanvasSprite/CanvasSpriteSystem idea might be a really good thing too. It is sorta what I'm working on anyway but I don't know that my knowledge of Three is good enough yet to take a stab at it.

Thanks for your help, by the way.

mrdoob commented 12 years ago

Uhm, it needs to be a flag like the material.overdraw = true, which is only for CanvasRenderer... Keep in mind that there are more renderers, as we need to keep the API "clean" :)

eldernos commented 12 years ago

I see the flag material.overdraw in the CanvasRenderer now. Is that being used for something? It seems like it always draws a triangle for a MeshBasicMaterial no matter what.

mrdoob commented 12 years ago

No no, material.overdraw doesn't fix your issue, I was saying that the flag we're talking about needs to be added just like that one right in the material.

eldernos commented 12 years ago

Okay, I've looked into it and I think I understand it but I thought I'd run it past you. Basically, it appears that if I use a basic mesh material with a face4 the canvas renderer passes that to the face3 render method and does two triangles instead of a quad. So, if I just add a field to the material saying it should override that and still use the face4 render method, it'll be good. Sound right?

eldernos commented 12 years ago

I made it work and it is very smooth with very low resource utilization but the texture is all white. Does a face four require a light source or a normal mapping?

mrdoob commented 12 years ago

I think you need a custom version of CanvasRenderer for your project. What you need is easy to make it work, but it's not the usual use case. Neither I can think of a clean way to allow that in the renderer.

You basically need only Face4, right? Neither you'll ever see these Face4 in perspective (meaning rotated in an X or Y axis or camera not in perpendicular).

If that's the case, feel free to zip what you have and I can give you back a quickly hacked CanvasRenderer that does what you need.

eldernos commented 12 years ago

I actually got it to work. I just had the canvasRenderer call "clipImage" if the material variable was set to "material.largeTexture = true" instead of calling the drawTriangle. So, just an added field to the material and an extra check in the renderer.

I can understand if you don't want to roll that into the engine but I'll send you a "BufferedCanvasRenderer" that shows what I'm doing in case you think there is a place for it in the long run.

Thanks for your help!

eldernos commented 12 years ago

Just wanted to show you the finished product... I think the performance here is plenty good enough for a 2d game, at least strategy games and probably side scroller if the background could be buffered and scrolled.

http://hurricanejs.zombienationonline.net/frontend/demo/demo4/techdemo.html

I can get the character to move quickly without any jumping and rotate and zoom the camera at the same time without the CPU going over 5% and memory stays stable. Pretty exciting.

mrdoob commented 12 years ago

Sweet! :)