08.24

A little description of the versatile Light class I’m using in pyromancer.

First there’s the basic version. It contains a range, a color and flags. It uses by default a fast but poor looking intensity/distance distribution. This is used by static lights computed during dungeon generation, but it does not look as bad because a diffusion post-processing algorithm is applied :

The first flag, RANDOM_RADIUS, makes it possible to have a variable radius. That’s nice for explosions and not-so-round fireballs :

The trick is to use a 1D noise indexed by the angle for a smooth radius variation. I use the noise from angle -7/8 xPI to angle 7/8 x PI and a lerp operation to avoid discontinuity near -PI.

The second flag, INVSQRT, use a more realistic 1 / Sqrt(dist) distribution (see my previous post about this). I’m using this for the smaller but more powerful fireballs that explode into deadly embers :

Now there’s an ExtendedLight that provides more features, including intensity varying over time and color varying over distance. The color varying stuff uses two more fields : a second color and a variation pattern in the form of a string of digits. 0 represent color1, 9 color 2. Between 0 and 9, a linear interpolation between both colors. There are plenty ways to use this feature. The obvious one is to have varying color depending on the distance from the light. Here, reddish to blueish :

But if you use black as second color, new possibilities arise, like rings :

Showing the intensity variation over time would require animated gifs so you’ll have to wait for Sept.18 to see it in the game. Basically, it adds two more fields : loop duration and intensity pattern. The intensity pattern uses a string of digits, like the color pattern. If duration is 5 seconds and pattern is “012345678987654321”, the light will slowly flash every 5 seconds. There’s a last trick. If the pattern value is “noise”, it uses a 1D noise for a smooth, random, non looping intensity variation. The incandescence spell, a static cloud of fire that lasts several seconds, uses this variation.

The source code for all those effects (except invsqrt which has been added recently) is available in the old 0.1.2 version of pyromancer :

https://roguecentral.org/doryen/svn-pyromancer/src/light.hpp

https://roguecentral.org/doryen/svn-pyromancer/src/light.cpp

On the 1D noise lerp thing: Another way to produce a clean cyclic value is to compute it on the surface of a unit circle (i.e. the value for an angle theta is the value where a ray at that angle from the origin intersects a unit circle). In my opinion, this produces noise more consistent with the base noise method than the interpolation approach. (And, of course, at higher dimensions this is pretty much unavoidable: To produce noise on the surface of a sphere, or in all directions in 3-space, you need a 3D noise function, because it’s much harder to find a function to “smooth out” the artifacts as you approach the poles.)

yeah you’re right, but that means using a 2D noise. Since there’s a call for every pixel (or rather subcell) of every since fireball on the screen, I get a huge performance boost by using a 1D noise, and most of the balls are so small that you cannot see the lerp fix.

For a bigger light, covering a big part of the screen, I would probably have to use your method.

Looks great! The way of specifying profiles in ExtendedLight reminds me of a class I meant to implement in the tutorial, but have scrapped in favor of a simpler alternative. It would be called Curve and you’d build it by specifying a list of setpoints: (x0,y0), (x1,y1), (x2,y2) … It would represent a generic curve that passes through those points. Then you could evaluate it at a given x, like y=curve(x). It wouldn’t really be curvy, but linearly interpolate between each pair of points.

Anyway it would be useful for all sorts of things, for example monster/item probability as a function of dungeon level, damage as a function of character level, etc. And it could also be used for light intensity as a function of the distance to the light center, as you did 🙂 Maybe you can repurpose your system for other uses like the ones I talked about.

yeah indeed, could be used for pretty much any single variable function… sounds a good idea for libtcod…

TCODFunction func = new TCODFunction(“0122234555678889”, 60.0f); // curve and variable range

return func->get(40.0f);