-
Notifications
You must be signed in to change notification settings - Fork 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
TerrainExtension (2/3) #7605
TerrainExtension (2/3) #7605
Conversation
a17bad4
to
f1ae39b
Compare
|
||
const MAX_SIZE = 2048; | ||
|
||
// TODO - import from loaders when Tileset2D is split out |
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.
FYI @ibgreen - I'm not importing the types from geo-layers
because geo-layers
currently depend on extensions
. TypeScript really does not like circular dependencies. Also a good argument to strip Tileset2D
out of that increasingly complex module.
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.
As long as you use import type
you might be OK, as TypeScript does support for circular import type
declarations.
But I agree we should try very hard to avoid this circularity as it is really confusing and will lead to other things creeping in down the line.
BTW, I got a good way on the PR separating Tileset2D from Viewport but then ran into issues:
- both node and browser tests are badly broken on Apple M1, so I really couldn't iterate in an efficient way, replying on CI takes like 10-15 minutes per cycle.
- Also there is one part where the Tileset2D interacts back and forth with the viewport which makes it hard to make the required values static input parameters, would be great to get your thoughts on that.
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.
+1 on moving Tileset2D/TileHeader
into core
if (!terrainFittingMode) { | ||
// props.extruded is used as an indication that the layer is 2.5D | ||
// @ts-ignore `extruded` may not exist in props | ||
const is3d = this.props.extruded as boolean; |
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 want to note here that MVTLayer
renders a single SolidPolygonLayer
for both extruded and non-extruded polygons (by returning 0 from getElevation
). This makes it impossible to render the park layer as draped and the building layer as offset. We have discussed separating features by MVT layer, and this is an example where it is necessary.
a19edd8
to
5bafaed
Compare
c48da99
to
92c03e3
Compare
5bafaed
to
7cc72aa
Compare
7cc72aa
to
5e15705
Compare
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.
Some quick nits from first read through.
|
||
const MAX_SIZE = 2048; | ||
|
||
// TODO - import from loaders when Tileset2D is split out |
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.
As long as you use import type
you might be OK, as TypeScript does support for circular import type
declarations.
But I agree we should try very hard to avoid this circularity as it is really confusing and will lead to other things creeping in down the line.
BTW, I got a good way on the PR separating Tileset2D from Viewport but then ran into issues:
- both node and browser tests are badly broken on Apple M1, so I really couldn't iterate in an efficient way, replying on CI takes like 10-15 minutes per cycle.
- Also there is one part where the Tileset2D interacts back and forth with the viewport which makes it hard to make the required values static input parameters, would be great to get your thoughts on that.
return layers; | ||
} | ||
|
||
/** If layer is the descendent of a TileLayer, return the corresponding tile. */ |
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.
This is pretty deep knowledge about layer types... Do we may want to elevate some coreTileLayer
into the canonical set of "building block" layers (Layer, CompositeLayer, ...)
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.
That may not be a bad idea...
|
||
const MAX_SIZE = 2048; | ||
|
||
// TODO - import from loaders when Tileset2D is split out |
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.
+1 on moving Tileset2D/TileHeader
into core
useInPicking = true; | ||
|
||
/** true if inside a picking pass */ | ||
private isPicking: boolean = false; |
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.
Given that useInPicking
is set on Effect
, would it not be better to move isPicking
there also and then have it set externally rather than inferring from the Pass?
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.
Technically this flag is not a property of the effect, but a property of the current pass. What we need is an isPicking
in PreRenderOptions
, just like in LayerFilterContext
. I'm not making that change here because it's going to be a much more extensive API change. All these bits of information about the current render pipeline are scattered and slightly different when passed to each effect method, layer method, pass method, and user callbacks. We need to do a thorough review of the Effect interface in v9.0, now that we have a lot more use cases to consider than when it was first designed.
Given that we are very close to the 8.9 release, I propose that we don't rush to another big API overhaul prematurely. At least this is a self-contained module and will be easy to refactor when we're ready.
...opts, | ||
pass: 'terrain-height-map', | ||
viewports: [viewport], | ||
layers: terrainLayers, |
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.
Another worry about updating the viewport is that slight changes in the viewport will result in a different rasterized heightmap. Thus offset features will wobble as the camera is moved. An extreme example is a thin column terrain layer offseting points. Sometimes the sample will hit and other times miss
Screen.Recording.2023-02-02.at.13.14.22.mov
Perhaps the heightmap could be rendered into a viewport defined in the coordinate space of the terrain layer?
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.
It was a hard decision to make. There is only one height map for all layers. If we index the height information by common space coordinates instead of clipspace coordinates, then for high-pitch scenarios, the resolution closer to the camera will be very bad. I can try construct a viewport from the pitch, bearing and the common bounds, which could be more stable. The shadow module suffers from the similar problem, and honestly it's never completely solved.
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.
It would be great if at least when panning we could avoid the glitches. I know it is a hard problem to solve, one approach I've used in the past was to quantize the viewport calculations (e.g. round to the nearest degree/zoom) so that small changes don't cause a heightmap re-render. While this doesn't completely solve the issue it at least means there are fewer jumps.
I also have the same issue in the CollideExtension
where fading isn't completely smooth due to sampling precision. I've considered writing the visibilities into a intermediate buffer/texture and averaging over frames to smooth out the visibility value, but it is too much to fit into the current work, but perhaps something to consider as a future improvement.
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.
If we index the height information by common space coordinates instead of clipspace coordinates, then for high-pitch scenarios, the resolution closer to the camera will be very bad.
Superficially, this seems quite similar to the problems that arise for perspective shadow maps. The good news is that there are a wide range of techniques to address those.
Rendering the shadowmap in three zones (top thirds, middle third, bottom third), rendering by object distance, logarithmic shadowmaps etc. It is not hard to find good articles.
If my hunch is right, we might even be able to share such code with an improved shadow effect?
Now this implementation seems to have some nice advantages, it should have good performance characteristics for static layers, however for heavily animated layers or very dense scenes where the draw call count becomes very high with this method, it is not impossible that a more "traditional", non-tiled approach could be useful
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.
Thank you both for the suggestions. I'm interested in exploring this idea further in a future PR, especially as a reusable strategy in multiple effects. As it is now the height map texture is not bound to any viewport type (common space -> clip space projection is handled by its own shader, not the standard project module) which means we could potentially use regions of this texture differently to provide multi-tier resolution.
This PR is already huge as is. I acknowledge that various functionalities could still be improved, but I prefer that we land a good skeleton first then improve it incrementally.
...opts, | ||
layers: drapeLayers, | ||
moduleParameters: { | ||
dummyHeightMap: this.dummyHeightMap, |
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.
We are using this dummyXXXXMap
pattern in a number of effects. Would it be worth having a single global dummy map to avoid having to duplicate resources?
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.
@ibgreen Maybe this is a luma question - is it reasonable to add something like Texture.getDefaultTexture(device)
? These "dummy" textures are never read in the shader, just to make sure that all samplers are supplied for the model to render.
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.
It is indeed annoying that one cannot detect in the shader if a texture or sampler was actually supplied. Additional boolean uniforms tend to be used for this.
I suspect there is a perf advantage to the boolean uniform approach as it avoids the sample/memory read but the default texture idea is not bad for cases where this is not a perf bottleneck.
Supplying a default 1x1 texture would not be a problem. However we may need to check for WebGPU, where the setup is more rigid. No point in adding an interface in 8.9 that we have to retire in 9.0
e4d92cd
to
1e6a3d4
Compare
Fix case for terrain op only rename enum fix shader compilatione errors Fix layer culling when rendering to terrain cover Fix TS errors lint
881c74e
to
f999da0
Compare
SKIP: 5 | ||
}; | ||
|
||
const TERRAIN_MODE_CONSTANTS = Object.keys(TERRAIN_MODE) |
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.
If we were changing them with some frequency that could make some sense. But our projection system feels quite complicated now, The saved effort in not having to change these during an update to this system must be really small.
...opts, | ||
pass: 'terrain-height-map', | ||
viewports: [viewport], | ||
layers: terrainLayers, |
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.
If we index the height information by common space coordinates instead of clipspace coordinates, then for high-pitch scenarios, the resolution closer to the camera will be very bad.
Superficially, this seems quite similar to the problems that arise for perspective shadow maps. The good news is that there are a wide range of techniques to address those.
Rendering the shadowmap in three zones (top thirds, middle third, bottom third), rendering by object distance, logarithmic shadowmaps etc. It is not hard to find good articles.
If my hunch is right, we might even be able to share such code with an improved shadow effect?
Now this implementation seems to have some nice advantages, it should have good performance characteristics for static layers, however for heavily animated layers or very dense scenes where the draw call count becomes very high with this method, it is not impossible that a more "traditional", non-tiled approach could be useful
...opts, | ||
layers: drapeLayers, | ||
moduleParameters: { | ||
dummyHeightMap: this.dummyHeightMap, |
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.
It is indeed annoying that one cannot detect in the shader if a texture or sampler was actually supplied. Additional boolean uniforms tend to be used for this.
I suspect there is a perf advantage to the boolean uniform approach as it avoids the sample/memory read but the default texture idea is not bad for cases where this is not a perf bottleneck.
Supplying a default 1x1 texture would not be a problem. However we may need to check for WebGPU, where the setup is more rigid. No point in adding an interface in 8.9 that we have to retire in 9.0
@@ -224,6 +226,32 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT = {}> extends Laye | |||
}); | |||
} | |||
|
|||
getBounds(): [number[], number[]] | null { | |||
if (this.props._instanced) { |
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.
What is this prop?
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.
This was an experimental prop we introduced when working on tiled geometries. By default this layer interprets mesh.POSITION
as meter offsets from the anchor (instancePosition
) regardless of the anchor's coordinate system. If _instanced: false
then the layer only renders 1 copy of the mesh and mesh.POSITION
is interpreted as world space offset (i.e. in LNGLAT mode the vertex lnglat is calculated as instancePosition + meshPosition
). It's used by the i3s and terrain use cases. I agree that the prop name does not make much sense to the user, hence the underscore.
For #7195
See docs for API review.
Tests will be in the next PR.
Change List
getBounds
method inSimpleMeshLayer
to account for the non-instanced use case (used by theTerrainLayer
)