-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial work on 3D vision #13595
Initial work on 3D vision #13595
Conversation
What about diagonal+vertical offsets makes things harder? |
return !valid_move( p, below, false, true ); | ||
}; | ||
|
||
// Raycasting, as opposed to shadowcasting for 2D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raycasting is immensely slower than shadowcasting (and it has weird artifacts), I'm extremely skeptical that this will be a workable solution for anything other than working on the draw code. The 3d FoV code is going to need to be either shadowcasting, or something more sophisticated than shadowcasting. Probably we can get by with shadowcasting plus some z-level aware fixups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's why I am going for the part 5 with it first - just for sight and attacks, not as the FoV function.
Updating shadowcasting to 3D would require some clever algorithm - either extending shadowcasting to 3D (which would probably be a totally new algorithm) or tolerating some simplifications and asymmetries during z moves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, it was late and I missed that part of your summary.
I think extending shadowcasting to 3D is doable, I have a mental model for it in my head.
The shadowcasting algorithm works by finding spans of adjacent squares within an octant and processing them recursively. It does this by iteratively considering each square and deciding whether to add it to the current span, or use it as the start of a new span. As an optimization (and the source of the name) spans with no visibility are not processed. Before the algorithm processes a new span, it recursively processes the previous span.
To extend the algorithm to 3D, the algorithm would need to find 3D spans (frustums) with uniform transparency, it can do this by iterating along a major and minor axis, when a change in transparency is found, the algorithm will need to split the existing frustum along the major axis (creating a frustum with depth along both the major and minor axis) and split the previous segment of the current iteration along the minor axis (creating a frustum with no width along the major axis, and 0 or more width along the minor axis), the algorithm would recurse both frustums, then proceed to the new span. I can see it in my head, but it's really hard to explain :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean.
I had a similar idea.
Well, I'm not sure if I get what minor and major axes are here, but otherwise I think I get it.
If minor and major axes are arbitrarily chosen (like now y vs x in castLight
) rather than depending on some factor, then I'm pretty sure I get it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One final check if I really get it:
We want to split the currently processed projection/rectangle/etc. into 3 parts:
- Rectangle that is already processed
- Rectangle that has one line processed, rest unprocessed. The line ends with (before? after?) a point with change in transparency
- Rectangle that is unprocessed
Then extend a frustum (another recursive call) from each of those rectangles.
Right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, this gets more impressive by the day. |
A horizontal move has a known vision check (check transparency of the destination tile), vertical move also has a known vision check (existence of the floor on the upper tile). |
I'll split the |
OK, split the bresenham and line_to into the old one and the modified one. Added an option to see critters below the player, but it requires either the Added drawing a symbol for "critter is below here". A green 'v' on cyan background. Only in fov_3d and debug modes for now. |
I'm still not clear on the need for a modified bresenham, I'll read the code and try to figure it out, but I figured I'd try and clarify in prose first. The underlying problem is that horizontally we're using a cell or voxel model, but we're not using that model vertically, things that aren't cells can block vertical movement. Therefore it's not sufficient to check the identity of the target square. |
Just horizontal+vertical, whether the horizontal part itself is diagonal or not doesn't really change anything.
Mostly this.
Moving (walking) is a relatively easy case, as it can be assumed that the higher point has a floor. Additionally, the horizontal move still has to be checked. This leads to an additional split that looks like this:
Which would further slow down processing of "diagonally vertical" lines. |
Awesome stuff, @Coolthulhu! |
I think I see, boiling it down to having floating floors above the player's head (between z-levels 0 and 1), how do you decide which squares you can interact with via vision or a thrown object. If there is a ceiling immediately above you, does that mean you can't interact with the 8 squares horizontally adjacent to you but one square up? Similarly, if there is a ceiling above each adjacent square, but not one directly above you, can you still interact with those squares? Intuitively the second must be true, since it corresponds to a player standing in a pit and trying to interact with things adjacent to the pit. the question is, do we also want the first to be true, I think so.
I don't think there's a good way around that, your alternative extends the length of the path, which means it processes more squares instead of slightly increasing the cost per-square. I think if deciding if 0,0,0 (source) can see/attack 1,0,1 (destination), you need to do something like:
|
Now I've got a design problem: I'm passing an array of pointers to 2D arrays, but that's kinda ugly. Any better way? Preferably (but not necessarily) not involving redesigning the entire cache or dropping the ability to use the same function to project vision and lights. |
If you redeclare the cache as a You might have to cast the resulting pointer to pass it as a sized array instead of as a raw pointer, haven't checked. |
The implementation seems to be going well, but I still got some bugs to fix. I iterate over y, then z, then x. This means that the area is processed in squares/rectangles up to d tiles tall and up to d tiles wide, where d is the square distance to player. A given rectangle being processed looks like this:
When I encounter a transparency change, I recurse on A, B and C, then process D in current function. Anything I'm doing wrong? Should I document the algorithm somewhere? |
That looks right.
I assume in your example there was a transparency change between B and D.
At this point I'd suggest recursing on A and B, trying to merge B and C is
likely more trouble than it's worth.
|
In case anyone is wondering why is it going so slowly, I'm having problems with what looks like the last edge case: What I figured out:
This is the worst kind of programming - "shotgun programming". I'm at the point where I'm changing (semi)random stuff and checking that it still doesn't work. |
Oh wow. All of this stumping me is expected, but this takes the cake. |
I caused a bug resembling this many times when fiddling with the shadowcasting code, it was always due to me accidentally resetting newStart. |
|
||
const auto mirror_pos = veh->global_pos() + veh->parts[mirror].precalc[0]; | ||
// Down | ||
cast_zlight<0, 1, 0, 1, 0, 0, -1, sight_calc, sight_check>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All calls to cast_zlight
have the same value for the template parameter xz
and yz
: 0. Is that intended? Is there actually any need for those parameters (extensions planed for the future)? Also, the // Up and // Down comments are strange, why are there 10 calls to cast_zlight
after the // Down, but only 6 calls to it below the // Up comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All calls to cast_zlight have the same value for the template parameter xz and yz: 0. Is that intended? Is there actually any need for those parameters (extensions planed for the future)?
I added them before I knew what I needed, so they may actually be useless. I thought I might be able to use them to map the small cones directly above and directly below the player, but it doesn't really sound like it would work.
Also, the // Up and // Down comments are strange
I got the comments wrong there, what matters is the zz
template parameter. // Up
should be above the first cast_zlight
with zz == 1
.
Conflicts: src/options.cpp
…into slow-3d-fov
While this isn't done, it performs tolerably for most cases. Current issues are:
|
Ohhhh cant wait to test this one :) On Sat, Jan 9, 2016 at 11:08 AM, Coolthulhu [email protected]
|
Segfault, go to an office tower in a release build, turn on debug mode, and try to look at z level 2. |
Always looking forward to more Z-level implementation. Very nice, Coolthulhu! |
Segfault isn't a problem with the new code, drawing a level the player isn't on breaks an assumption of some new code. (cata_tiles.cpp:1314) |
I'm hacking away at this, it ended up rather involved because there are a number of method calls that need a tripoint instead of a pair of x/y coordinates. |
I'm not sure if that statement is implying a cause or a hindrance towards finding said cause. |
Ah, nice to see that in. If I recall, this was the main thing keeping reality-devouring darkness ( #13786 ) in the "long term" pile. |
Ah was this PR to allow us to say..stand ontop of a tall building and see If so it dont work...well in TILES version :( On Tue, Jan 12, 2016 at 8:37 AM, Chaosvolt [email protected] wrote:
|
It lets you use 'x' to look around. you use "<" and ">" to look up and down. The walking around vision is coming, if I'm reading the op correctly. |
oh damn I got to test this out then. thanks On Tue, Jan 12, 2016 at 3:38 PM, Malkeus [email protected] wrote:
|
Hmmm i press x the <> but it dont work? On Tue, Jan 12, 2016 at 3:39 PM, daniel roberts [email protected]
|
did you enable 3d vision in the debug menu? |
It is disabled by default, even on z-level worlds. You need to explicitly enable it in options. |
I'm excited about this one. Does enabling this mean zombies will be able to see me and path to me across z-levels now? |
i got it to work i think You need to enable it in optoins and then enable DEBUG mode in game then I On Tue, Jan 12, 2016 at 3:50 PM, Malkeus [email protected] wrote:
|
See yes, but pathing may often be quite wonky. |
Mostly pushing so that I can get a design issue/question resolved:
Current
line_to
does diagonal moves. This is OK for x/y, but diagonal moves that include z-level moves make my job harder.Is it OK to have
line_to
split horizontal (x/y) and vertical moves into one-then-other? Is my version (the one in the diff) of splitting it correct?Now the actual summary of the PR:
map::build_map_cache
build transparency and outside caches for all z-levels (in z-level mode). This is actually a minor bugfix, as it could have been enabling some subtle (minor) bugs.Adds an option in debug tab. Setting it on enables the stuff below:
map::sees
andmap::find_clear_path
. Those work like 2D ones except they use 3D bresenham, and when making a z-move they check form.valid_move
to determine if it can actually be made.For now I'll be going for #6824 (sight and attacks) rather than #6821 (actual FoV itself), as the former is both more noticeable and easier to implement.