
Jules Bean wrote:
Hi,
I have just uploaded another little proof-of-concept reactive toy.
http://roobarb.jellybean.co.uk/~jules/crayon.8.tgz
The important part is the beginnings of an OpenGL accelerated 2D rendering engine, for rendering parametric curves and in particular bezier curves.
Jules, thanks for uploading the code! As a reactive newbie, examples are a gigantic help .. It works great .. about 40FPS on my ATI Technologies Inc RV515GL [FireGL V3350] using about 3% of my AMD Athlon(tm) 64 X2 Dual Core Processor 6000+ CPU. I had to make a small change to get it to work on my system (perhaps I'm down rev somewhere) .. but I thought I'd share in case other newbies have the same issue .. crayon.hs:689:57: Constructor `WeightedProperties' should have 4 arguments, but has been given 1 crayon.hs:699:8: Constructor `WeightedProperties' should have 4 arguments, but has been given 1 I just changed the WeightedProperties args in each case to, (WeightedProperties (_,p) _ _ _) from (WeightedProperties ((_, p) : _)) That seems to work. Thanks again, Tom
The way it works at the moment is:
* A sampling step tries to adaptively generate points on the curve to approximate it as a polygon. This is done on the CPU, isn't as clever as it could be, but turns out to be not the bottleneck right now * For stroked regions, this is 'thickened' out to a stroke using difference-based approximations to a tangent. This works well for smooth curves but poorly at end points and sharp corners. For filled regions the GLU tessellator is used to convert it to GL primitives, normally triangles. * Then this is rendered by openGL in either immediate mode or using VAs.
Currently the sampling and GLU tessellation can be cached and not redone every frame, if the appropriate part of the structure doesn't change each frame. You need to use standard GHC sharing to make this work - define constant picture fragments at a high enough scope that they get shared. The stroke generation is not cached and this turns out to be pretty expensive. You may need to compile with -fno-state-hack to get proper sharing (GHC "issue"/bug).
I discovered a bug in the HOpenGL GLU binding. I don't think this demo tickles the bug so it should run on a standard build, details of that bug on the hopengl list.
The immediate mode renderer turns out to be faster than the VA based renderer, at least for these small demos. I think that's because the extra pass over the data structure (to generate the VAs) is a relatively high cost, the number of vertices is small, and I'm not smart about sharing vertices.
I then plugged all this into Reactive and set up three simple animations - some spinning flowers, some business-like charts, and a classic pong game. You can switch between them using your left + right arrows keys, and they spin in + out using a simple animation, whilst their own animations continue independently. Exactly the kind of thing Reactive is supposed to make easy!
The framerate is artificially limited to save battery life when developing.
As before, this code uses my incomplete Reactive implementation not Conal's; it should be easy to port although I think some of my primitives have slightly different names.
I'm not going to work on this code for a bit, but I have in mind various possible extensions, including using the GPU to do some of the heavy lifting, using a fragment shader to support gradients and other kinds of procedural texturing, supporting implicit curves as well as parametric ones. I'm interested in any thoughts on how to design a useful attractive combinator library for 2D graphics.
The real target is to have something fast enough to make an expressive library for complex smooth animations, e.g. 2D games.
Jules
_______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive