clark Posted December 15, 2014 Share Posted December 15, 2014 There is actually a couple of questions here. Imagine a simple paint tool, what is the best (in theory) way to deal with the segments of drawing? There is 2 approaches: 1) You can see that a user has stroked. For example, between any 2 given frames, you can interpolate a circlular brush texture to a render texture. For example overlapping ooooooooooooooo between the brush strokes. This is good on desktop, but slowwwwwww on mobile. 2) You can do the same, but instead of interpolating a circular brush, you could create your 2 cicular points, and then place a rectangle between these points. I guess this is faster? But are there any other ways that I should consider before re-writing number one above? 3) What is the best way to deal with Dynamic colours? Is it better to have a brush texture (for example 12 colors, 12 brush textures?). I ask because in Starling we used GrayScale with Tint but the colours were washed out. Thanks! Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted December 15, 2014 Share Posted December 15, 2014 Why wouldn't you just draw a line with two rounded ends : ctx.lineCap="round"this way you could even use bezier or quadratic curves to smooth the transitions between points. Quote Link to comment Share on other sites More sharing options...
clark Posted December 16, 2014 Author Share Posted December 16, 2014 I cannot just use standard drawing. The "paint" comes out like toothpaste and so the brushes are almost bevelled. On top of that, one of the brushes needs to be bevelled but be a mix of colours. Quote Link to comment Share on other sites More sharing options...
rich Posted December 16, 2014 Share Posted December 16, 2014 Ohh.. I need to rush out now, but I'll happily post my method about this later! I've just finished an art package for the BBC which includes tinted brushes, textured brushes and all kinds of other painting effects - so it's pretty fresh in my mind clark 1 Quote Link to comment Share on other sites More sharing options...
rich Posted December 16, 2014 Share Posted December 16, 2014 But to answer your question quickly - I interpolate between the values as the user paints, yes. Quote Link to comment Share on other sites More sharing options...
clark Posted December 16, 2014 Author Share Posted December 16, 2014 Hey Rich that sounds cool Is this Aardman? Anyway, I will try to make it more digestible. This is what I am after, this uses an "Interpolation" method. It will look good when the textures are repaired which is out of my hands ATM. What is the best way to have one Brush Texture which can be dynamically coloured?As stated, in Starling this was awkward. We used a GrayScale image which was Tinted but this lost vibrancy. Colours tended to look a bit flat. Something to do with "Multiplying" colour tints on Gray Scale textures. There was also something preventing me from doing this easily. I think that the textures had to be pre-generated, a new SpriteSheet created, and then uploaded to the GPU otherwise ContextLoss would wipe out the brushes. I do not know how much of this stuff applies to Canvas/WebGL. What is the best way to achieve Interpolation? In my naive process, I find point A and point B which occurred on an update cycle, and I interpolate all of the one pixel points between these 2 points and then draw a circle on each of these pixel points. Here is a fast swipe which looks fine. I thought that this process is the performance killer on Mobile. It is painfully easy to swipe 300 pixels on an IPad within a single frame, that is 300 draw calls in the space of one update to the BitmapData. So it is this iteration count..... or how I am doing this operation which I feel is killing performance. However it is good to know that I am on the right track. Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted December 16, 2014 Share Posted December 16, 2014 when you say a mix of colors, are they blending like actual paints of is it simply a brush that has two colors at once? Quote Link to comment Share on other sites More sharing options...
clark Posted December 16, 2014 Author Share Posted December 16, 2014 I think a brush which has 2/3 colours at once (Not Blending). I think the result will kind of suck but my hand is forced. There is a few ways we were thinking 1) A circle split into 3 equal vertical parts2) A pie chart circle split into 3 parts (wedges)3) 3 small brushes grouped together perhaps even with a rotation but that probably looks pretty bad. I guess depending on dynamic colours, some of these options might be better suited than others. Quote Link to comment Share on other sites More sharing options...
rich Posted December 16, 2014 Share Posted December 16, 2014 Ok I'm back so can write a bit more now The way I handle the painting is to hook the tool to a mousemove callback (never an update loop, assuming Phaser of course) and then I do as you've suggested, so I iterate the distance from A to B over a pre-defined number of steps, every movement. Sometimes you only need one update, and sometimes if they're moving real fast you need a few more (mine is capped at 20). In either case I draw directly to a BitmapData as they move. If I wanted to limit this I could instead record the points and then only draw at a set interval - it won't be as smooth though. I handle dynamic coloured brushes two ways. If I can do it via Canvas paths then it's easy, you just define a shape and fill it (easy for circles or whatever). For textured brushes we carefully created the paint texture so it worked well with the way Pixi tints graphics. Each tool has its own "texture" BitmapData, which I clear off, draw the brush texture to, tint it and then when painting I just paint directly from that BMDs canvas. So it only tints when the colour changes. Some brushes have 2 layers.. a tinted mask layer and an overlay layer. Note that Pixi has a bug in it re: tinting on Android, but this is fixed in Phaser. The end result can often look quite cool Here's an example running on a black paper type: clark 1 Quote Link to comment Share on other sites More sharing options...
clark Posted December 16, 2014 Author Share Posted December 16, 2014 Thanks for taking the time to share this Rich, know you are a busy guy right now. These are things I need to look at: mousemove callback (never an update loop) I may use an update step (it is phaser). Ill check it Note: Yes, this has helped to increase the responsive nature of the input cheers for the tip! Sometimes you only need one update, and sometimes if they're moving real fast you need a few more (mine is capped at 20). With you until you said capped at 20. Does this mean that the maximum distance you interpolate between 2 updates is capped to 20 pixels per update? Unlike my 300 pixel example which is possibly the biggest problem for me. For example if the user moves 100 pixels in a swipe, do you fill that with spots in 20 iterations, or do you set the distance to 20? If I can do it via Canvas paths then it's easy, you just define a shape and fill it (easy for circles or whatever) Checking this out this evening thanks! For textured brushes we carefully created the paint texture so it worked well with the way Pixi tints graphics. Ah yes, this may be our old Starling problem with washed out colours. We used GrayScale but not "in a way that works well with tints" perhaps. This could explain dull colours. This gives me plenty to look into! The end result is awesome by the way! Quote Link to comment Share on other sites More sharing options...
rich Posted December 16, 2014 Share Posted December 16, 2014 The "20" cap means the maximum number of steps I make during the interpolation, per frame. So I don't step every single pixel difference, but divide the distance by (up to) 20 and then move based on that. Our paint textures are virtually all white. I've attached an example (although you may not see it, but it's below!) clark 1 Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted December 16, 2014 Share Posted December 16, 2014 For the tinting you should look into how the blend mode 'overlay' works. Draw the resulted tint to a buffer canvas and you should be good to go no matter what lib you're using. I'm going assume you are using the 2d context so my RGB values will be based on the range of 0-255. You'll probably have to write the code yourself but it gives much better results. Most systems just use multiply which is probably the easiest to implement:(r1*r2) / 255, (g1*g2) / 255, (b1*b2) / 255 but you loose some of the detail for lighter colors and dark colors are twice as dark. The opposite of that would be Screen which actually takes the negative value and blends it: (r1* -r2) / 255, (g1* -g2) / 255, (b1* -b2 ) / 255 which will always be lighter. But you loose the darks completely. So what Overlay does is it has a threshold, usually around 128 (or .5) and varies how it treats the combined colors based on that. It will either Multiply or Screen the color. Allowing you to have both shadows and highlights. Check out this link for the math of all the Photoshop blend modes. http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/ Overlay is the way to go if you ask me. clark 1 Quote Link to comment Share on other sites More sharing options...
clark Posted January 5, 2015 Author Share Posted January 5, 2015 Thanks for all your help guys! I am pretty much on the road to success. There is one final thing stumping me though, and that is patterns. The actual article for this image is here: http://perfectionkills.com/exploring-canvas-drawing-techniques/I kind of understand it maybe. The problem is that I have a sub texture in a Texture Atlas which is 128x128. I am trying to figure out how I can start off with this Phaser.Image and end up somehow being able to follow that link.So for example, it seems like I need a Phaser.Image, and then I will draw that to a BitmapData, and then create a HTMLImageElement set the src of that to toDataURL() of my BitmapData. And then I have an image which I can use with the context pattern method? Is this the right track do you think or am I missing something easier? Quote Link to comment Share on other sites More sharing options...
rich Posted January 5, 2015 Share Posted January 5, 2015 You can use a Canvas object as a fill pattern without turning it into an Image first. clark 1 Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted January 6, 2015 Share Posted January 6, 2015 @clark Here is a JSBin showing how easy it can be with a buffer canvas. http://jsbin.com/tozanefoka/1/In that example the canvas holding the pattern is actually in the DOM but it doesn't have to be. Normally I would have just used document.createElement('canvas') so its only available through code. clark 1 Quote Link to comment Share on other sites More sharing options...
omnivibe Posted January 7, 2015 Share Posted January 7, 2015 Here is some code for a fully working bitmap painting tool... seems to work really, really well across all WebGL devices: http://jsfiddle.net/Lk668xk0/1/ HOWEVER, I can't figure out how to erase. Please help! (see line 163 in the JS) Quote Link to comment Share on other sites More sharing options...
clark Posted January 8, 2015 Author Share Posted January 8, 2015 So you guys are all pretty awesome ! I was very limited before by process. I was unsure as to the features of the canvas because I had never directly used it, and also, I was unsure about how Phaser and the drawing methods gelled together. But I finally figured it out! Just the simple fact that Phaser.BitmapData IS a Canvas basically and knowing that is my friend. So there are 2 methods which are needed to cover almost all possible drawing tools. 1) An iterative process (I was trying to stuff everything into this technique). Where you build a texture from colours/overlays/whatever, and then you paint that final result iteratively between 2 points. I still am unsure if I will like performance, but for now its the best bet. 2) A stroke based process using native canvas. Pens, Pencils, Crayons, Spray Tools, Patterns, Erasing.... This is much faster than draw() and covers the majority of otherwise awkward functionality. You can also use method 2 to construct a brush for method 1. For example, imagine a Pie chart multi colour brush. Draw the colours with drawing API, overlay it onto a Shape, and then use that outcome as the brush. Luckily, for the most part, these 2 features work with similar API. Although one uses textures and one uses geometry. None the less, you can cover a vast amount of stuff. Also "Canvas for Dummies" never hurts anyone!Thanks for your help with this guys, appreciated. Omni Vibe: For Erasing, I used this simple code: Given that you know what the currentX/Y and previousX/Y (all tools need this but this is real demo code) and have access to a BitmapData (or other 2d context), it is as simple as this. And this is really smooth and looks better than the iterative method on devices. Although I am troubled reading that browsers may be a problem. In that case, I will need to use a clearRect or translated Line. if (previousX === 0 && previousY === 0) { previousX = currentX; previousY = currentY; } //dst is a reference to a bitmap data which is the main canvas we draw on var ctx: CanvasRenderingContext2D = dst.context; ctx.globalCompositeOperation = "destination-out"; ctx.beginPath(); ctx.moveTo(previousX, previousY); ctx.lineTo(currentX, currentY); ctx.lineWidth = 30; ctx.lineCap = "round"; //set the stroke style to 100% transparency, may not work on all browsers? ctx.strokeStyle = "rbg(0, 0, 0, 1)"; ctx.stroke(); //remember to dirty it because otherwise you see jack on webGL dst.dirty = true;You may want to store the original globalCompositionOperation in a variable and return it after dst.dirty, but I just set the correct operation on a per tool basis. Quote Link to comment Share on other sites More sharing options...
rich Posted January 8, 2015 Share Posted January 8, 2015 You can see an one of the art tools we built here: http://www.bbc.co.uk/cbeebies/makes/hey-duggee-hey-duggee-doodle (warning: might be BBC geolocked, sorry) clark 1 Quote Link to comment Share on other sites More sharing options...
clark Posted January 8, 2015 Author Share Posted January 8, 2015 Great stuff Sound really makes a big difference on your buttons and it is silk smooth! Quote Link to comment Share on other sites More sharing options...
rich Posted January 8, 2015 Share Posted January 8, 2015 If you check the Resources list in dev tools you'll see it pulls in a config.json file from the skins folder. Have a look at it - that is how the BBC re-skin the entire app Quote Link to comment Share on other sites More sharing options...
clark Posted January 8, 2015 Author Share Posted January 8, 2015 We are actually doing something similar to you this past year in terms of configuration. The engines we make can be customisable via forms. For example Hidden Object can be mapped out by an operator and X levels and themes and so on. I am finding however, that making games, and making customisable games are worlds apart in complexity. Quote Link to comment Share on other sites More sharing options...
omnivibe Posted January 19, 2015 Share Posted January 19, 2015 For Erasing, I used this simple code: Given that you know what the currentX/Y and previousX/Y (all tools need this but this is real demo code) and have access to a BitmapData (or other 2d context), it is as simple as this. And this is really smooth and looks better than the iterative method on devices. Although I am troubled reading that browsers may be a problem. In that case, I will need to use a clearRect or translated Line. if (previousX === 0 && previousY === 0) { previousX = currentX; previousY = currentY; } //dst is a reference to a bitmap data which is the main canvas we draw on var ctx: CanvasRenderingContext2D = dst.context; ctx.globalCompositeOperation = "destination-out"; ctx.beginPath(); ctx.moveTo(previousX, previousY); ctx.lineTo(currentX, currentY); ctx.lineWidth = 30; ctx.lineCap = "round"; //set the stroke style to 100% transparency, may not work on all browsers? ctx.strokeStyle = "rbg(0, 0, 0, 1)"; ctx.stroke(); //remember to dirty it because otherwise you see jack on webGL dst.dirty = true;You may want to store the original globalCompositionOperation in a variable and return it after dst.dirty, but I just set the correct operation on a per tool basis. This seems very specific to Phaser... any idea how to translate that into PIXI (esp for WebGL)? Quote Link to comment Share on other sites More sharing options...
rich Posted January 19, 2015 Share Posted January 19, 2015 It can't be done in WebGL, you need to use the dest-out composite operation (which is specific to Canvas, not Phaser). So for WebGL you'll have to be using a canvas as a texture anyway. Quote Link to comment Share on other sites More sharing options...
omnivibe Posted January 20, 2015 Share Posted January 20, 2015 So for WebGL you'll have to be using a canvas as a texture anyway. Thanks, can you explain that line a bit-more in depth? I thought Canvas and WebGL were two different things completely? Quote Link to comment Share on other sites More sharing options...
omnivibe Posted January 20, 2015 Share Posted January 20, 2015 I checked out the BBC demo by the way (SSH tunelling through the cheapest DigitalOcean UK Droplet, hehe)... Seems the erasing isn't true erasing, just painting white? 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.