RPTools / maptool

Virtual Tabletop for playing roleplaying games with remote players or face to face.
http://rptools.net
GNU Affero General Public License v3.0
799 stars 261 forks source link

Support for DND 5e additive darkvision #1895

Open Merudo opened 4 years ago

Merudo commented 4 years ago

Is your feature request related to a problem? Please describe. In 5e, Darkvision is additive: it turns Darkness into dim light, and dim light into bright light.

This type of vision cannot be currently implemented in MapTool.

Describe the solution you'd like A extra parameter to sight, add#rrggbb, which would add the #rrggbb code to every light source seen by the token.

JamzTheMan commented 4 years ago

Are you going to add Pathfinder darkvision in as well? 😸

It's only black/white (greyscale) vision and would look really cool for sight to be reduce to greyscale! Of course, it's only within range (normally 60/90/120ft) and any other light source in the area would overwrite/overpower/replace this within the lights range...

JamzTheMan commented 4 years ago

Oh and Pathfinder 2E lowlight (and other DnD versions) bump dim to bright as well. Also, Darkness spells reduce bright to dim and dim to dark. But we can't do "that" right now (dim to dark, or dark to dim)

melek commented 4 years ago

I use dark red personal lights for darkvision (which washes the color out pretty well), and a bluish gray value for dim light; Something that has always irked me a bit in MT is that lights combine into darker colors, so my 'dim light in darkvision' looks darker than either dim light or darkvision.

They should add together to make brighter areas, which would resolve some of the weirdness.

MT Light (Subtractive): image

Actual Light (Additive):

JamzTheMan commented 4 years ago

@Phergus ya, why doesn't MapTool do it this way? Although, aren't we using RGB for lights and isn't that "additive" vs "subtractive" and should end up white?

I remember googling similar things doing the "color picker" for VBl and holy crap did I go down a rabbit hole and basically found out colors are hard lol (started getting into what "we" perceive as color and similar colors and some complicated algorithms for it. ended up just using a guesstimate instead)

Phergus commented 4 years ago

My memories on this are fuzzy at best and I haven't looked at the code to see how light colors are being combined. Given what we see in MT it wouldn't surprise me if we are doing a simple (R1+R2)/2 and so on which of course won't preserve luminance levels.

melek commented 4 years ago

Whatever is happening in MT right now is pretty weird. Here are some values of the pure colored lights, 'Night' vision mode over pure white background drawing (which appears gray because of the slight night coloring).

// MT Definitions
Green: circle 20#00ff00
Red: circle 20#ff0000
Blue: circle 20#0000ff

// White base color under darkness, in RGB / HSL
White BG: RGB 210, 210, 210 / HSL 0° 0% 82% 

Red:   255, 155, 155 /   0° 39% 100%  // Pure colors, brighter than white base
Green: 155, 255, 155 / 120° 39% 100%
Blue:  155, 155, 255 / 240° 39% 100%

R+G:   194, 155,  94 /  37° 52%  76%   // Combined colors, dimmer than white base
R+B:   194,  94, 155 / 323° 52%  76%   // Why do these skew in favor of red/blue?
G+B:    94, 155, 194 / 203° 52%  76%

R+G+B: 157,  94, 118 / 337° 40%  62%  // Center. Muddy Red; Should be GRAY or WHITE
JamzTheMan commented 4 years ago

Took a quick peek, we're doing a graphics2d.fill()

So a quick google search led me to adding the setXORMode line above the render block here:

newG.setXORMode(Color.black);

    for (Entry<Paint, List<Area>> entry : renderedLightMap.entrySet()) {
      newG.setPaint(entry.getKey());
      for (Area area : entry.getValue()) {
        newG.fill(area);
      }
    }
    timer.stop("lights-5");
    newG.dispose();
  }

And I got the below results... Promising. I don't have time to chase it down further... We'll have to probably render the lights like this elsewhere in the code before adding the background image or something, but looks like we can at least set the coloring to additive a bit better...

image

vs the original

image

cwisniew commented 4 years ago

You might want to try two overlapping lights of same colour before you go too far down the XOR path :)

What you will want to do is implement AlphaComposite and set the setComposite on the graphics context you are rendering lights to (should be separate image to map then final result drawn on map).

for simple case draw each of the lights on this new image black with alpha 0 newRed = existingRed + lightRed (clamped to max value so it doesn't overflow) newGreen = existingGreen + lightGreen (clamped to max value so it doesn't overflow) newBlue = existingBlue + lightBlue (clamped to max value so it doesn't overflow)

Then you need to decide what to do with the Alpha's there are three choices 1) Min value of existing and light (but use light where existing = 0). 2) Max value of existing and light. 3) Average of existing and light (but use light where existing = 0). Probably need to try all three and see which is more aesthetically pleasing for several overlapping lights. But option 3 would give you the most distinction with multiple overlaps (doesnt mean it will look best). Definitely dont add the alphas.

bubblobill commented 4 years ago

Since you are fiddling with lights, I would suggest accommodating gradients for those that enjoy realism. Not saying you should implement gradients, just that if implemented they would work with any changes.

lamp: circle 20#ffffdd>#000000    linear
lamp: circle 20#ffffdd>>#000000   inverse square
lamp: circle 20#ffffdd>>>#000000  inverse cube
JamzTheMan commented 4 years ago

Since you are fiddling with lights, I would suggest accommodating gradients for those that enjoy realism. Not saying you should implement gradients, just that if implemented they would work with any changes.

lamp: circle 20#ffffdd>#000000    linear
lamp: circle 20#ffffdd>>#000000   inverse square
lamp: circle 20#ffffdd>>>#000000  inverse cube

Think there is another issue for this but i like your notation...

melek commented 4 years ago

Yeah I made that issue for the same reasons @bubblobill suggested; figured it might be possible to accommodate if someone was messing with lights anyway ;)

I didn't want to dog-pile this issue too badly but I can't say I wasn't tempted.. #1908

Merudo commented 4 years ago

Another topic that might be relevant: Strange lighting when coloured light sources overlap

rkyeun commented 3 years ago

When light_source_1 of color <0.7 0.7 0.7> overlaps light_source_2 of color <0.5 0.0 0.0> the resulting color should be <1.0-((1.0-0.7)*(1.0-0.5)) 1.0-((1.0-0.7)*(1.0-0.0)) 1.0-((1.0-0.7)*(1.0-0.0))>.

When light_source_1 of color <0.7 0.7 0.7> overlaps light_source_2 of color <0.5 0.0 0.0> and a map art pixel with color <0.2 1.0 0.2> the resulting color should be <1.0-((1.0-0.7)*(1.0-0.5)*(1-0.2)) 1.0-((1.0-0.7)*(1.0-0.0)*(1.0-1.0)) 1.0-((1.0-0.7)*(1.0-0.0)*(1.0-0.2))>.

In general, inverse colors, multiply all overlaps together, inverse again. This makes the lights compound together.

When dark_source_1 of color <0.7 0.7 0.7> overlaps dark_source_2 of color <0.5 0.0 0.0> the resulting color should be <(0.7*0.5) (0.7*0.0) (0.7*0.0)>.

When dark_source_1 of color <0.7 0.7 0.7> overlaps dark_source_2 of color <0.5 0.0 0.0> the resulting color should be <(0.7*0.5) (0.7*0.0) (0.7*0.0)> and a map art pixel with color <0.2 1.0 0.2> the resulting color should be <(0.70.50.2) (0.70.01.0) (0.70.00.2)>.

In general, multiply all overlaps together. This makes the darknesses compound together.

When light_source_1 of color <0.7 0.7 0.7> overlaps dark_source_2 of color <0.5 0.0 0.0> and a map art pixel with color <0.2 1.0 0.2> the resulting color should be <(1.0-((1.0-0.7)*(1-0.2)))*(0.5*0.2) (1.0-((1.0-0.7)*(1.0-1.0)))*(0.0*1.0) (1.0-((1.0-0.7)*(1.0-0.2)))*(0.0*0.2)>

In general, run both the light and dark independently so each gets its dose of the map color, then multiply the results together. This makes the light and dark compound together.

To create a light source gradient, you would need to include better transparency, going from #RRGGBB to #RRGGBBAA. The problem with the inverse square falloffs is that they never have a maximum range, they just keep falling off. So if you were going to make one you'd need a scaling factor, not a radius, indicating the radius where the light source is at some specific percentage of its strength. And the machine would have to look out to infinity every step checking for lights to know how far they fell off by the time they got there. You'd be making raytracing engines at that point, which would probably lag the touch-fuzzy-get-dizzy out of everything. A spherical increasing or spherical decreasing falloff would be better, giving you a predictable and finite radius while still tricking the eye into looking like normal falloff.

adyoungblood commented 2 years ago

I've got an example working, implementing a Composite class as Craig mentioned. It additively blends colors as expected, and in conjunction with the optional #rrggbbhex option for Sights resolves the original issue as well. I'll touch up the code and get a pull request going as soon sometime this weekend. image

rkyeun commented 2 years ago

Neat! I had completely forgotten about this.


From: Alexander Youngblood @.> Sent: Wednesday, January 12, 2022 11:03 PM To: RPTools/maptool @.> Cc: rkyeun @.>; Comment @.> Subject: Re: [RPTools/maptool] Support for DND 5e additive darkvision (#1895)

I've got an example working, implementing a Composite class as Craig mentioned. It additively blends colors as expected, and in conjunction with the optional #rrggbbhex option for Sights resolves the original issue as well. I'll touch up the code and get a pull request going as soon sometime this weekend. [image]https://user-images.githubusercontent.com/16092423/149268850-9c13dac2-cc84-4c10-ae59-a79eeea2cf2a.png

— Reply to this email directly, view it on GitHubhttps://github.com/RPTools/maptool/issues/1895#issuecomment-1011798807, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AUW6TZJ7DL2HLMLK2EMNJP3UVZMKHANCNFSM4NGLJ2UA. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub. You are receiving this because you commented.Message ID: @.***>

Phergus commented 2 years ago

Testing. Colored lights are now additive but is perhaps why darkness doesn't render at all.

Player View - Three low-saturation colored lights image

Player View - Three low-saturation colored lights. With Darkness in center. image

GM View image

See also #1550, #2094, #3346

Phergus commented 2 years ago

This issue was originally about making Darkvision enhancing existing lighting. Making Darkness -> Dim and Dim -> Bright. With current dev code on a GM client, light sources are brighter when no tokens are selected and get dimmer whenever any token (with or without Darkvision) is selected. On a player client the same thing occurs.

Phergus commented 2 years ago

Going to leave this open as original request was not addressed.

Phergus commented 2 years ago

Player view token with Darkvision: image

Player view token with Normal vision: image

GM View: image

bubblobill commented 11 months ago

@kwvanderlinde Is this sorted now?

kwvanderlinde commented 11 months ago

Nope, although this FR was written in a time where dim/bright were primarily expressed by colours rather than lumens. So I don't think the OP really applies anymore, though the FR as a whole is still useful to keep around.

What we need for real darkvision is some kind of lumens-boosting capability. E.g., within 60' of a token with darkvision, all dim +50 lumens lights should become bright +100 lumens lights.