TheDuckCow / godot-road-generator

A godot plugin for creating 3D highways and streets.
MIT License
328 stars 17 forks source link

Implement lane transitions #4

Closed TheDuckCow closed 1 year ago

TheDuckCow commented 1 year ago

At the moment, one RoadPoint of a number of lanes (e.g. 3) can connect to another with a different number of lanes (e.g. 4), however the geometry abruptly will change.

Screen Shot 2022-10-16 at 12 10 59 AM

The goal is to instead create geometry to smoothly transition between two road points with different lane counts. Additionally, we should do our best to have at least decent texture transition in this phase. There are likely to be some artifacts given how the texture is currently sourced however, so we'll need to minimize the UV artifacts or identify alternative routes.

See for reference this Blender file which shows a crude plan on an approach for this geometry, and how it could work with the current texture / UV layouts. The nice thing about this approach is that there is no new material needed, and there's a clear way to interpolate smoothly by adding more loop cuts up until the final triangle within each segment. However there are some abrupt edge transitions, and risks for texture blurring.

mesh_plan_03_blend.zip

Screenshot below, with wireframe turned on:

Screen Shot 2022-10-16 at 12 17 14 AM
TheDuckCow commented 1 year ago

We are in the middle of quite some good discussion points. One of these, is how to position new lanes and centering around the RoadPoint. Copying from my recent email:

I like the idea of being able to seamlessly add road segments to one side or the other and keep the double yellows centered. I could also see where it might not be necessary, to keep the RoadPoint in the geometric center. I think it actually should be an option. See this illustration below:

This being said, we can start with one and see about later supporting the other approach. Let’s keep it simple to begin with. The logic would also need to include: What if there is no change in direction to center about? What if there are multiple reverses? I’m ok with whatever fallback we think is appropriate.

I’m not saying we have to do it this way, so let me know what you think.

Crude mocukup:

road_gen_center

bdog2112 commented 1 year ago

I like the possibilities pictured in the above example. It would be convenient to simply check a box that says "Align Divider" with the expectation that a divider must be defined either explicitly by the user or by some other decipherable parameters.

Also, if there are road points preceding the "aligned" road point, then they may need to be re-aligned when the "Align Divider" checkbox is toggled.

It's important to note that the center example represents two things: 1 Road point position has moved, 2 Lanes have been offset inside the segment.

Another thought is that maybe the alignment could occur as more of an "operation" on the road point position in the 3D viewport rather than on the lanes in the road point. (A hybrid version of the center and right examples.)

In other words, the user selects a road point and clicks an "Align Divider" button. In this scenario, the road point is automatically moved only once, during edit, rather than repeatedly evaluating a flag and moving the segment in a pseudo runtime fashion.

A "Lane Offset" parameter could also potentially be used. In this scenario, the lane offset alters the position of the lanes relative to the road point position. But, it doesn't move the road point position. Then, the user explicitly places the road point where they want in order to achieve the desired visual result.

One final thought: A curve runs through the center of each segment. Is its position critical for traffic AI? The center and right images both depict different locations for the curve relative to the center of the segments. What are the implications of both for traffic AI? Do they matter? Is one preferable over the other?

Afterthoughts: Just noticed that, in the center example where the start point lane offset changed, the end point lane offset also changed. Not sure if that was supposed to be the result of toggling the start point's "Align Divider" setting or if the setting was toggled separately on the end point. Depending on how that works, a single toggle could trigger a chain of events.

bdog2112 commented 1 year ago

Let's broaden this discussion to another important topic: NUMBER OF LANES

We've talked, briefly, about the possibility of having multiple lanes transition down to a single lane road. However, it appears that the minumum number of lanes is currently 4.

Users can specify fewer than 4 lanes. But, the road width (not to be confused with "lane width") won't adjust, accordingly. Although, the lane stripes do update to indicate fewer (and much wider) lanes. Is that intentional?

When the lane transition work is completed, should the road generator be able to display a single lane or is that a feature that we want to save for a future version of the program?

TheDuckCow commented 1 year ago

We can chat more about this live, but some initial comments as I've taken a look:

I do see there is some odd behavior, but it has to do with transitioning from one RoadPoint with one number of names to the next. Se here how I am able to get from 2 to 4 to 5 lanes (the code intentionally doesn't let you go down or up by more than 2 extra lanes at a time between two RoadPoints):

Screen Shot 2022-11-06 at 9 19 31 AM

In terms of funny behavior, is right now it's not very predictable how the transitioning lane segment looks, sometimes it's textures/count of lanes of geometry are defined by the "prior" or the "next" RoadPoint. When implementing the lane transition, I suppose it will make sense to have the geo start from the side of the larger number of lanes (in terms of texturing etc), and then shrink it down as it gets closer to the one with the smaller count. Whereas right now, it seems somewhat arbitrary what it's picking for what defines how the 'transitioning' segment looks like. For instance in the screenshot above, I absolutely could not get the texture to look normal, even when manually assigning the markings.

So if anything is looking odd right now, try adding another RoadPoint before or after with the same count to see if you can get a road segment of the intended size/number of lanes.

For the one-lane scenario: I see there indeed are some bugs, complaining about an index which appears to just prevent the shoulder on one side from drawing.

Screen Shot 2022-11-06 at 9 26 10 AM

We can look at addressing the 1-lane case in the future, since it will need to be special I suppose. Let's get the geo working first for the 2+ lane to 3+ lane transitioning.

TheDuckCow commented 1 year ago

I like the possibilities pictured in the above example. It would be convenient to simply check a box that says "Align Divider" with the expectation that a divider must be defined either explicitly by the user or by some other decipherable parameters. Another thought is that maybe the alignment could occur as more of an "operation" on the road point position in the 3D viewport rather than on the lanes in the road point. (A hybrid version of the center and right examples.)

In other words, the user selects a road point and clicks an "Align Divider" button. In this scenario, the road point is automatically moved only once, during edit, rather than repeatedly evaluating a flag and moving the segment in a pseudo runtime fashion.

I don't love the idea of shifting nodes around underneath. We can't stop users from parenting other items to the roadpoint that would get messed up. I think an offset via flag is not unreasonable, as we are already calculating and shifting from one side to the next along the orthogonal of the curve source anyways.

A "Lane Offset" parameter could also potentially be used. In this scenario, the lane offset alters the position of the lanes relative to the road point position. But, it doesn't move the road point position. Then, the user explicitly places the road point where they want in order to achieve the desired visual result.

This makes more sense to me.

One final thought: A curve runs through the center of each segment. Is its position critical for traffic AI? The center and right images both depict different locations for the curve relative to the center of the segments. What are the implications of both for traffic AI? Do they matter? Is one preferable over the other?

Nope, traffic AI will entirely be decided by Lane curves (which don't yet exist). That's a future effort we don't need to worry about right now. So the point of having Align Divider is more for artistic and precise placement control.

Afterthoughts: Just noticed that, in the center example where the start point lane offset changed, the end point lane offset also changed. Not sure if that was supposed to be the result of toggling the start point's "Align Divider" setting or if the setting was toggled separately on the end point. Depending on how that works, a single toggle could trigger a chain of events.

I'm thinking this toggle would be a per-roadpoint setting (with network-level buttons that allow you to easily change them all at once). In the example I showed, I just adjusted it to show what it would mean if the Align Divider setting were on, or off, for all the displayed roadpoint at once. In practice, it could toggle from one roadpoint to the next (and so that amount of offset around the divider would also be lerp'd as you move along the RoadSegment)

bdog2112 commented 1 year ago

Hey @TheDuckCow,

Here are some Lane Transition "rules" to discuss in our meeting, tomorrow. Think of this as a requirements list and feel free to add/remove/update requirements.

The lane drawing algorithm will assume that:

TheDuckCow commented 1 year ago

Hey @bdog2112, I worked on the scenarios doc, let me know if you can think of any missed scenarios below (based on your suggested rules above), or if there are any contradictions.

Road Transition Scenarios v01.pdf

I added the directional arrows as indications of the LaneSegments we'll eventually create. That's not part of this task, but rather for https://github.com/TheDuckCow/godot-road-generator/issues/17. LaneSegments (which will be barely-extended curve objects) shouldn't impact this task at all, but likely this is a decent time to align on it.

bdog2112 commented 1 year ago

Great writeup! These images help to illustrate many possible scenarios and provide good test cases.

In the context of the lane transition work, some clarification is needed on the following items: 1 "One-way" and "Both" lane configurations 2 One-way road transitions 3 Align to center 4 "Auto-texturing"

"ONE-WAY" AND "BOTH" LANE CONFIGURATIONS Do we intend to support "one-way" and "both" lane configurations in the lane transitions update?

ONE-WAY ROAD TRANSITIONS If a road is one way and it moves from one lane to two or more, then which side gets the transition(s)? What about "both" lane configurations?

ALIGN TO CENTER Page one refers to an "align to center" option (not to be confused with the fact that lanes are always centered on the road point). This may have also been referred to, previously, as "align divider".

Is this in scope or out of scope for the lane transitions release?

AUTO TEXTURING In the past, we said that transition lanes won't have any lines. The primary reason for this is to avoid stretching and another goal is to insure that textures are seamless.

That having been said, we obviously WILL texture transition lanes. We just need to be clear about what we will and won't do. At present, the intent is to give transitions the default road color, which is gray, and to never give them a non-solid color because that would show visible stretching and wouldn't tile seamlessly.

In short: If we create a transition, we will texture it.

In the PDF: What is meant by "auto-texturing"? What is the difference between a dark green/red transition and a gray one? Should all transitions be gray? Alternatively, should only invalid transitions be gray?

TheDuckCow commented 1 year ago

[edited from initial post as I realize the quotes got mingled with my responses]

Do we intend to support "one-way" and "both" lane configurations in the lane transitions update?

Great to clarify, no, I do not expect anything touching on one-way streets to be in the initial scope, long term goal only.

If a road is one way and it moves from one lane to two or more, then which side gets the transition(s)?

Always put the transition on the "outside". One way to think of this is that a one way road is really a two lane road, with 0 revere lanes and 1+ forward lanes (so transition goes on right), or 1+ reverse lanes and 0 forward lanes (so transition goes on left). This will neatly tie in the with the user interface design, where the user is dragging the middle divider left and right. Dragging this middle point shouldn't suddenly pop in transitions on different sides once the reverse goes to 0.

What about "both" lane configurations?

Generally, both I expect to really only be valid for single lane scenarios, and so they would always transition to directional lanes which will then determine the "outside" and thus where transitions fall. Again, out of scope for now.

Page one refers to an "align to center" option (not to be confused with the fact that lanes are always centered on the road point). This may have also been referred to, previously, as "align divider".

Yes sorry, this is referring to the previous "align divider" feature indeed. Let's not worry about align divider yet, I still believe it will be an easy update after the fact, but no need to worry about it now. Let's just always make the lanes be geometrically centered for simplicity.

In short: If we create a transition, we will texture it.

Agreed with the points mentioned. I'm ok with essentially what you described before, where we take the transition lane UV and make it smaller and fit it inside the gray area of a square. If we want to support global textures like asphalt, a) we'd probably want to follow some kind of global base texture anyways and b) way out of scope, so let's not fret over it.

In the PDF: What is meant by "auto-texturing"? What is the difference between a dark green/red transition and a gray one? Should all transitions be gray? Alternatively, should only invalid transitions be gray?

Auto texturing means that, if the auto texturing tick box is on (which essentially we expect it always would be), then it is able to figure out the lanes and placement of the UVs for continuous textures. Gray means it couldn't, and defaults to just texturing as gray. As we talked, the lane transitions in reality will also be gray, so I just marked lane transitions as slightly darker for red/green to illustrate that But still making it visually obvious it relates to the)

Unless you see it differently and that all transitions wouldn't be gray. From your last diagram, the shoulder white line would be part of the shoulder segment and not the transition segment, meaning I don't think we'd have any markings in the transition right?

Let me know if anything still needs clearing up!

bdog2112 commented 1 year ago

Hey @TheDuckCow,

You asked to see some code pertaining to lane matching. So, I've put together a "match_lanes" stub. I'll paste the code, below, Then, attach a tiny working Godot project folder in case you want to view it in the editor. This is a work in progress and, by no means, complete. But, it's pretty close. I can walk you through it on Thursday. Feel free to comment, sooner, if you have some thoughts to share.

In this example, "lane matching" means looking at a segment's start and stop points, matching the lanes from the traffic direction flip locations, and generating a new lane list that includes transitions.

The lane matching filters need further polishing/hardening. Each filter places a lane into one of the following buckets: MATCH, TRANSITION_ADD, TRANSITION_REM(ove). The trick will be massaging the filters to properly assign lanes to all of the buckets.

There are 4 lane configuration scenarios in the project and it's easy to add more. Some of them produce the expected outcome while others do not. As previously mentioned: work in progress.

Please see the attached code. I'll put it in a separate comment just to keep things neat.

bdog2112 commented 1 year ago

Update: Algorithm is working! Resolved an issue with FORWARD lane matching. Also, temporarily made it so that matched lanes display a result of "_", which makes it much easier to differentiate the transition lanes. Trying to break the algorithm. No luck, so far, which is good! Tested 11 different lane configurations.

godot-road-generator-lane-match-stub-03.zip

------------------------------------------------------------------------------

func _match_lanes() ->Array:

TTD: Consider validating road point traffic direction.

# 1st element should always be REVERSE.
# Traffic direction should only flip once per Road Point.
# We can forego validation if we trust that inputs are always valid.

#Get lane flip offsets for start and end points. The offset represents the
#ID of the first FORWARD lane on a road point. Should always be > 0.
var start_flip_offset = 0
var end_flip_offset = 0

for i in range(len(start_point.traffic_dir)):
    if start_point.traffic_dir[i] == RoadPoint.LaneDir.FORWARD:
        start_flip_offset = i
        break

for i in range(len(end_point.traffic_dir)):
    if end_point.traffic_dir[i] == RoadPoint.LaneDir.FORWARD:
        end_flip_offset = i
        break

# Exit if no flip occurred. Calling routine should check for empty array
# to determine if it should draw the segment.
if start_flip_offset == 0 or end_flip_offset == 0:
    push_warning("Warning: Unable to match lanes.")
    return []

# Build lanes list.
# Build by iterating and matching REVERSE lanes. Then, FORWARD lanes.
# First, match by iterating backward from position of lane flip. Then,
# match by iterating forward from position of lane flip. When the start
# point or end point is missing a lane, create a transition.
var lanes:Array

# Match REVERSE lanes.
# Iterate the start point REVERSE lanes. But, iterate the maximum number of
# REVERSE lanes of the two road points. If the iterator goes below zero,
# then assign TRANSITION_ADD lane(s). If the iterator is above -1 and
# there is a lane on the end point, then assign the start point's LaneType.
# If the iterator is above -1 and there are no more lanes on the end point,
# then assign a TRANSITION_REM lane.
var start_end_offset_diff = start_flip_offset - end_flip_offset
var range_to_check = start_flip_offset - max(start_flip_offset, end_flip_offset) - 1
for i in range(start_flip_offset-1, range_to_check, -1):
    if i < 0:
        #No pre-existing lane on start point. Add a lane.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, TRANSITION_ADD" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check])
        lanes.push_front(RoadPoint.LaneType.TRANSITION_ADD)
    elif i > -1 and i - start_end_offset_diff < 0:
        #No pre-existing lane on end point. Remove a lane.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, TRANSITION_REM" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check])
        lanes.push_front(RoadPoint.LaneType.TRANSITION_REM)
    else:
        #Lane directions match. Add LaneType from start point.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, match %s" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check, start_point.lanes[i]])
        #lanes.push_front(("-")start_point.lanes[i])
        lanes.push_front("_")

# Match FORWARD lanes
# Iterate the start point FORWARD lanes. But, iterate the maximum number of
# FORWARD lanes of the two road points. If the iterator goes above the
# length of start point lanes, then assign TRANSITION_ADD lane(s). If the
# iterator is below the length of start point lanes and there is a lane on
# the end point, then assign the start point's LaneType. If the iterator is 
# below the length of start point lanes and there are no more lanes on the
# end point, then assign TRANSITION_REM lane(s).
range_to_check = max(len(start_point.traffic_dir), len(end_point.traffic_dir) + start_end_offset_diff)
for i in range(start_flip_offset, range_to_check):
    if i > len(start_point.traffic_dir) - 1:
        #No pre-existing lane on start point. Add a lane.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, TRANSITION_ADD" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check])
        lanes.append(RoadPoint.LaneType.TRANSITION_ADD)
    elif i < len(start_point.traffic_dir) and i - start_end_offset_diff > len(end_point.traffic_dir) - 1:
        #No pre-existing lane on end point. Remove a lane.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, TRANSITION_REM" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check])
        lanes.append(RoadPoint.LaneType.TRANSITION_REM)
    elif i < len(start_point.lanes):
        #Lane directions match. Add LaneType from start point.
        print("i %s, sed %s, sfo %s, efo %s, rtc %s, match %s" % [i, start_end_offset_diff, start_flip_offset, end_flip_offset, range_to_check, start_point.lanes[i]])
        #lanes.append("-")start_point.lanes[i])
        lanes.append("_")

return lanes

--End Match Lanes-------------------------------------------------------------

bdog2112 commented 1 year ago

Hey @TheDuckCow,

The lane matching routine should have produced the expected results in our meeting this morning. It seems that I may have inadvertently tweaked a variable while walking you through the code.

In the Godot IDE output excerpt, below, the result was correct when the program first started. Then, during the walkthrough, the "Set name" output appeared several times, probably due to something that was typed or clicked.

After that, the result was incorrect. The excerpt, below, was copied verbatim from the IDE. Upon reloading the scene, everything was fine, again. I was unable to recreate the "Set name" output. (Gremlins?)

Godot IDE output excerpt:

Godot Engine v3.5.stable.official (c) 2007-2022 Juan Linietsky, Ariel Manzur & Godot Contributors.
--- GDScript language server started ---
Switch Scene Tab
scenario is: 3 RRFF > RFF
i 1, sed 1, sfo 2, efo 1, rtc -1, match 1
i 0, sed 1, sfo 2, efo 1, rtc -1, TRANSITION_REM
i 2, sed 1, sfo 2, efo 1, rtc 4, match 1
i 3, sed 1, sfo 2, efo 1, rtc 4, match 0
LaneTypes to draw: [3, _, _, _]

Set name
Set name
Set name
scenario is: 3 RRFF > RFF
i 1, sed 1, sfo 2, efo 1, rtc -1, match 1
i 0, sed 1, sfo 2, efo 1, rtc -1, TRANSITION_REM
i 1, sed 1, sfo 2, efo 1, rtc 4, match 1
i 2, sed 1, sfo 2, efo 1, rtc 4, match 1
i 3, sed 1, sfo 2, efo 1, rtc 4, match 0
LaneTypes to draw: [3, _, _, _, _]
TheDuckCow commented 1 year ago

Thanks @bdog2112 - as mentioned, would be great to have the code you wrote pushed into a module, and then if needed I'd be happy to huddle with you to show how we could make some parametric tests using GUT (example here). I realize also that testing so-far with this plugin is actually incredibly light, we have a lot more in the Wheel Steal project which I could show you.

Please make a branch for this isolated effort of the lane matching algorithm. As we discussed:

If you want to move on to other work after this lane matching effort, you would create that in another "branch" which is chained on top of the lane matching one. We can cover that if needed later.

bdog2112 commented 1 year ago

The lane matching routine is actually coded so that it can be copy pasted directly into road_segment.gd and work right off the bat. Hence, I would recommend against using it in a stand alone file due to the greater complexity and additional effort.

However, the stand alone file was easier to edit with the caveat that you had to attach the script to an object in a scene file and there was also extraneous support code needed in order to make it run. Why don't we put the extra files (gd and tscn) in the addons folder, for the time being. But, also paste the routine into road_segment. Does that sound reasonable?

Having a stand alone GUT test sounds great! I'd like to see all possible combinations of 2-5 lanes tested for good measure. ;-)

TheDuckCow commented 1 year ago

I'm a strong advocate of not having copied code sitting in two places - so go ahead and directly place it in the road segment as you suggest, and then work on the tests. After the tests have been implemented, we can decide if it still makes sense to have separate demo gd/tscn demo files or not. Even in a demo scene, I'd still have it reference the road segment class directly to avoid duplication, instead of copying the code block. Remember: good test code also acts as documentation!On Dec 15, 2022 11:10 PM, bdog2112 @.***> wrote: The lane matching routine is actually coded so that it can be copy pasted directly into road_segment.gd and work right off the bat. Hence, I would recommend against using it in a stand alone file due to the greater complexity and additional effort. However, the stand alone file was easier to edit with the caveat that you had to attach the script to an object in a scene file and there was also extraneous support code needed in order to make it run. Why don't we put the extra files (gd and tscn) in the addons folder, for the time being. But, also paste the routine into road_segment. Does that sound reasonable? Having a stand alone GUT test sounds great! I'd like to see all possible combinations of 2-5 lanes tested for good measure. ;-)

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

bdog2112 commented 1 year ago

I totally agree with your sentiments that it's better not to have duplicate code and I agree with the solution you've proposed.

The purpose of this post is simply to highlight the pros and the cons of various solutions and, hopefully, emphasize the real goal, which is, probably, to make development easier and faster. ;-) Call this a philosophical discussion.

What is the real goal?

As I'm coding, I will continue to look for ways to help improve the Road Generator codebase and keep searching for the thing hinders live edits on lane_segment. My gut tells me that it has to do with dependencies on road_point_gizmo. But, that's just an instinct and it could be totally wrong. If/when I discover something, I'll let you know.

bdog2112 commented 1 year ago

Hey @TheDuckCow. I've created a Lane Matching branch and uploaded the new routine and some GUT tests. I'll upload a more comprehensive battery of tests tomorrow. But, for now there are two test cases: 1 RF > RF, 2 RFF > RRF to provide a sampling.

bdog2112 commented 1 year ago

"Lane Matching" is one piece of "Lane Transitions". But, there is still more to do!

bdog2112 commented 1 year ago

Hey @theduckcow,

Checkout the latest on lane transitions!

High Level Update:

Detailed Update:

TRANSITION FUNCTIONALITY ON REGULAR LANES When a road segment's start and end points have a differing number of lanes, transition lanes are automatically added, which is great! Lanes are centered on the start and end points.

The drawing routine required "lane direction" in order to know when to flip textures on REVERSE lanes. Updated the lane matching routine to include lane direction in the outputs.

The "_rebuild" routine imposed a limit of 2-lanes-difference beteween the start and end points. Removed this limit since transitions will automatically fill in the difference.

Lines look nice and clean when the updated road texture is used. However, it seems that a 1px "margin" may not be enough. When zoomed way out, geometry still sometimes exhibits slight bleeding from outside of the designated UV tiles. It's nothing egregious. But, it should be preventable with adequately spaced texture margins.

RESOLVED "JAGGY" BUG When segment start and end points had differing lane widths, jaggies appeared (see screenshot below). Updated the interpolation algorithm and transitions are now smooth as butter.

Added "demo2.tscn" to illustrate jaggy issue and resolution. We should remove it before the final commit since we've already got "demo.tscn".

Screenshot: Jaggies appeared when start/end point lane widths differed Clipboard02

Screenshot of 2 to 4 lane transition: Clipboard01

TheDuckCow commented 1 year ago

Great progress indeed @bdog2112! Looking forward to having the shoulder back in, but that is already a big step up as-is.

Can you share the texture you currently are using locally? I'll take this as an opportunity to update it in the repo as well and implement the extra pixel buffer as you suggest. I'm not seeing the full glory as I realize I still have the old style texture locally.

One other thing I note is that a little rough bit at the edge, not sure if this is preexisting though. Here I have manually moved upwards this one roadpoint so the other ends curve upwards to this middle one.

https://user-images.githubusercontent.com/2958461/210024758-2c87ce31-7232-4327-95e8-fb4dd0839d34.mp4

Edit: Also want to show off that there is smooth interpolation, Bret's screenshots abvoe just have the magnitude values set to 0 (and ignore my odd texture issues, for the same reason as above as my local file isn't using the new layout convention yet)

Screen Shot 2022-12-29 at 4 28 37 PM
bdog2112 commented 1 year ago

Can you share the texture you currently are using locally? I'll take this as an opportunity to update it in the repo as well and implement the extra pixel buffer as you suggest. I'm not seeing the full glory as I realize I still have the old style texture locally.

Here is the texture I'm currently using: road_texture

Drop it into your project and it should look incrementally better. At some point, it may be worthwhile to review the texturing goals and see if it would be beneficial to update UV coordinates.

Typically, a "margin" of, maybe, 1-3 pixels is recommended between UV tiles. The margin should extend the colors that it surrounds. That way, if there's any bleed, the rendered texture will just bleed the margin color. Depending on your use case, this may solve your texturing issues. ;-)

(Consider texture baking a UV tile in Blender: Your UV tile gets filled with the baked texture. The "margin" surrounds the outer border of the tile and, basically, just repeats the border colors.)

bdog2112 commented 1 year ago

One other thing I note is that a little rough bit at the edge, not sure if this is preexisting though. Here I have manually moved upwards this one roadpoint so the other ends curve upwards to this middle one.

I think I see what you're talking about and I would attribute the anomaly to interpolation. It's conceivable that there is another issue at play. But, I don't think so.

Remember: The start and end loops are rigidly constrained to the angle of the start and end points. But, everything in between is interpolated and each loop is aligned perpendicularly to the curve.

What that means is that the 2nd loop can overlap the 1st loop and so on because the drawing routine simply aligns each loop to the curve. The user can take this into account when making adjustments. Alternatively, the developer can place constraints that limit/prevent unwanted results.

Now, you're talking more about loops being angled upward as opposed to overlapping. But, the same rules apply: Loops are aligned to the curve. Hence, if the curve is rotated, slightly, then all of the loops must rotate in order to compensate.

At the moment, I don't think there is anything buggy going on. Just unwanted side effects of the user's freedom to rotate in any direction. What are your thoughts?

We could, potentially, add a helper adjustment for managing unwanted side effects. The bezier handle adjustment is VERY helpful for curve width tweaks. But, not as helpful for curve height tweaks.

Maybe we could use an additional rotation tweak value that, somehow, smooths out the X-axis rotation of the start and end loops. Something that forces the "almost" end loops to conform more closely to the "actual" end loops. ;-) It could even be tied to the bezier handle. The higher the value, the greater the effect of the rotation tweak value.

bdog2112 commented 1 year ago

Something else to keep in mind is that some curve implementations allow the user to adjust every individual loop of the curve. (Think Blender.) Hence, the user has much greater control over the final result.

TheDuckCow commented 1 year ago

You make a good point, it is probably just an interpolation issue. Let's leave it as is for now, but something we can consider separately in the future is a trick used for good deformation in animated meshes, where extra sampling/geo is made on the curvier parts of the mesh. That's a whole other wrench in how we are doing things now, so I'll post it as another non-prioritized issue in the meantime.

Thanks for the texture, totally missed you added the yellow dotted line, a great touch that makes the most of the layout. I'll work on updating the template with this as a reference 👍

bdog2112 commented 1 year ago

Food for thought:

Godot's "Curve3D" object has a "Tesselate" function that fits your description for good deformation:

PoolVector3Array tessellate(max_stages: int = 5, tolerance_degrees: float = 4) const

Returns a list of points along the curve, with a curvature controlled point density. That is, the curvier parts will have more points than the straighter parts...

Obviously, we're already setting the curve density and deciding where to sample it for drawing our loops. But, perhaps tesselate could provide some benefits in the overall shape of the curve.

bdog2112 commented 1 year ago

Added shoulders and gutters back into road segment drawing routine. It's looking good!

There is a long standing issue with inconsistent shoulder line drawing (see old screenshot below). One shoulder has a fat line and the other has a fluctuating thin line. Currently investigating...

Screen Shot 2022-11-06 at 9 19 31 AM
TheDuckCow commented 1 year ago

Uploading an image to use in the godot asset library:

transitions widgets