Skip to content
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

Refacto limit dependency on layeredmaterial #792

Merged
merged 2 commits into from
Jul 13, 2018

Conversation

peppsac
Copy link
Contributor

@peppsac peppsac commented Jun 26, 2018

2 commits, related to the same context: stop leaking internal attributes (WMTS level, LayeredMateiral texture indexing schema) in all iTowns.

First commit content:

fix: stop using 'level' property where it's not mandatory

Currently the 'level' concept from WMTS has leaked everywhere in iTowns
codebase.

This commits replace its usage in one part of iTowns, by something
equivalent but that doesn't force texture Providers to mock WMTS level
mechanism.

2nd one is:
refacto(core): avoid manipulating LayeredMaterial internal state

This commits is a step forward stopping to rely on LayeredMaterial
internal representation of textures (using an index and such) in other
parts of iTowns.

This will allow to reuse all this code for objects that don't use a
LayerMaterial instance as their material.

@peppsac peppsac force-pushed the refacto_limit_dependency_on_layeredmaterial branch from ed80474 to 9bb0578 Compare June 26, 2018 13:16
Copy link
Collaborator

@mbredif mbredif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely goes in the right direction !
I made a few comments.

@@ -224,8 +219,10 @@ TileMesh.prototype.getCoordsForLayer = function getCoordsForLayer(layer) {
} else {
return OGCWebServiceHelper.computeTMSCoordinates(this, layer.extent, layer.origin);
}
} else {
} else if (layer.extent.crs() == this.extent.crs()) {
return [this.extent];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this test go into Extent.as ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could but actually the as() function should be improved to support a 'no-clone' argument (currently we always clone the extent, even if the requested crs is the same).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add some comment to explain your choice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -169,7 +169,7 @@ function tileAt(pt, tile) {
return t;
}
}
if (tile.getLayerTextures(l_ELEVATION)[0].coords.zoom > -1) {
if (tile.material.textures[l_ELEVATION][0].coords.zoom > -1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it equivalent to isElevationLayerLoaded ? (which would be clearer)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

I'll use this but I'll remove it from TileMesh and move it where it belongs = LayeredMaterial.

@@ -11,10 +11,12 @@ import RendererConstant from '../Renderer/RendererConstant';
import OGCWebServiceHelper, { SIZE_TEXTURE_TILE } from '../Provider/OGCWebServiceHelper';
import { is4326 } from './Geographic/Coordinates';

function TileMesh(geometry, params) {
function TileMesh(layer, geometry, params) {
// Constructor
THREE.Mesh.call(this);
Copy link
Collaborator

@mbredif mbredif Jun 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the call to super could pass directly the geometry and material : super(geometry, material) (with a material constructed at the call site) and the constructor could be more explicit with : TileMesh(geometry, material, extent, level), as in 6dd1ba5

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Constructor
THREE.Mesh.call(this);

this.layer = layer;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the semantics of this layer ? which one is it ? I would rather keep it outside of TileMesh as with other meshes in itowns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the GeometryLayer, and as stated above it was already stored in TileMesh (and I think it's useful).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where tile.layer is used?

Copy link
Contributor Author

@peppsac peppsac Jun 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance here.

And I'll repeat myself: this isn't a new thing. I simply moved the layer assignement inside the constructor.

@@ -37,6 +39,7 @@ function TileMesh(geometry, params) {
this.frustumCulled = false;

this.updateGeometricError();
this.wmtsCoords = {};
Copy link
Collaborator

@mbredif mbredif Jun 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes more sense to move this to Extent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I moved it here, again because it was assigned somewhere else (https://github.com/iTowns/itowns/pull/792/files#diff-5e0253bd7132719c7fe2322c297efe16L48).

I propose to keep it that way for this PR, and use your PR #785 as an opportunity to fix this since it's more focused on the extent side. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not rename it to a more appropriate name in the same time ?

@@ -66,7 +66,7 @@ function initNodeElevationTextureFromParent(node, parent, layer) {
elevation.max = max;
}

node.setTextureElevation(elevation);
node.setTextureElevation(layer, elevation);
node.material.elevationLayersId =
parent.material.elevationLayersId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you removed it...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you still support material.elevationLayersId ? I tought you proposed to remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok. I pushed a commit removing this line.

opacity: layer.opacity,
fx: layer.fx,
idLayer: layer.id,
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you create this intermediate object instead of using layer directly ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this intermediate object from elsewhere, but you're right it's useless. I'll remove it.

};

LayeredMaterial.prototype.setLayerVisibility = function setLayerVisibility(index, visible) {
LayeredMaterial.prototype.setLayerVisibility = function setLayerVisibility(layer, visible) {
const index = Number.isInteger(layer) ? layer : this.indexOfColorLayer(layer.id);
this.uniforms.visibility.value[index] = visible;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we deprecate the index-based api (with a console.warn) ? is it still used elsewhere ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only used internally in LayeredMaterial.

There are a bunch of other index based method in LayeredMaterial and I think they should all be removed. I'll do a separate PR for this stuff.

return mat.getColorLayerLevelById(layerId) > -1;
const textures = mat.getLayerTextures(layer);
if (textures.length) {
return textures[0].coords.zoom > -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not it be textures.every(texture => texture.coords.zoom == this.level) or textures.every(texture => somefun(layer, this.extent, texture.coords) for a suitable function ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it doesn't matter, since we always set textures in one shot (this is what getColorLayerLevelById was doing anyway)

@@ -371,7 +371,7 @@ function _readZ(layer, method, coord, nodes, cache) {
}

const tile = tileWithValidElevationTexture;
const src = tileWithValidElevationTexture.getLayerTextures(l_ELEVATION)[0];
const src = tileWithValidElevationTexture.material.textures[l_ELEVATION][0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tileWithValidElevationTexture.getLayerTextures(layer)[0] ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no, because layer here is the geometry layer. And the calling site is not aware of which elevation layer we can use.

So for now I'll leave it like that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and with the famous tile.layer?
you could write:
tileWithValidElevationTexture.getLayerTextures(tile.layer)[0]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and with the famous tile.layer?

Are you sure the famous bit is needed?

you could write: tileWithValidElevationTexture.getLayerTextures(tile.layer)[0]

And it would be incorrect. tile.layer is the GeometryLayer, not the Layer used for the elevation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok,
So we don't know in elevation layer the data is searched for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add tests to DEMUtils, if it doesn't exist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the scope of this PR. I'm only changing this file to remove one useless abstraction (and it's a 2 line diff)

@peppsac
Copy link
Contributor Author

peppsac commented Jun 27, 2018

Pushed a commit which should fix the above issues. Thanks for the review @mbredif :)

Copy link
Contributor

@gchoqueux gchoqueux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also add the tests on the layeredMaterial

// Constructor
THREE.Mesh.call(this);

this.layer = layer;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where tile.layer is used?

return this.material.loadedTexturesCount[l_ELEVATION] > 0;
const textures = mat.getLayerTextures(layer);
if (textures.length) {
return textures[0].coords.zoom > -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use EMPTY_TEXTURE_ZOOM instead of -1

@@ -224,8 +219,10 @@ TileMesh.prototype.getCoordsForLayer = function getCoordsForLayer(layer) {
} else {
return OGCWebServiceHelper.computeTMSCoordinates(this, layer.extent, layer.origin);
}
} else {
} else if (layer.extent.crs() == this.extent.crs()) {
return [this.extent];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add some comment to explain your choice

currentElevationLevel >= 0 &&
(node.level - currentElevationLevel) >= maxDeltaElevationLevel) {
return false;
const currentTexture = node.material.textures[0][0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use l_color instead of 0

const offsetScale = node.material.offsetScale[0][0];
const ratio = offsetScale.z;
// ratio is node size / texture size
if (ratio < 1 / Math.pow(2, maxDeltaElevationLevel)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a ratio, between the tile's extent and texture's extent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

currentElevationLevel >= 0 &&
(node.level - currentElevationLevel) >= maxDeltaElevationLevel) {
return false;
const currentTexture = node.material.textures[0][0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part of this process is identical to the globe processing, it should be encapsulated in one place. Duplicate code is harder and longer to maintain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate code is harder and longer to maintain

This is not an absolute truth, and here I disagree. This is a 5 line copy-paste so I don't think it hurts to keep them separate.

If you really want to improve code sharing between part of itowns I opened a PR sometimes ago: #674

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the duplicate code is technical debt

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate code is harder and longer to maintain

Abusive factorization too, by introducing another layer of abstraction.

the duplicate code is technical debt

The program without technical debt is the one with 0 codes. I agree duplicate code has a cost, but introducing another function has one too. I call it more or less even here. But as it is not the scope of this PR and as this snippet is quite short, maybe you can let this one go?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function can be factorized:

  • as an OGC conversion tool. there is WMTS_WGS84Parent. Why not have the opposite function?
  • it's an identified conversion tool, so no code duplication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this code is a hack to workaround a bug in the SSE logic (= computing the geometric error using a sphere - except that a sphere is a poor approximation for a square tile).

So I'd rather get rid of it, instead of transforming it in a API that would be used in other places of iTowns.

@@ -28,7 +28,7 @@ export default {
if (node.layerUpdateState[c.id] && node.layerUpdateState[c.id].inError()) {
continue;
}
if (c.tileInsideLimit(node, c) && !node.isColorLayerLoaded(c.id)) {
if (c.tileInsideLimit(node, c) && !node.isColorLayerLoaded(c)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if isElevationLayerLoaded is material function, why isColorLayerLoaded is tileMesh function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly because this PR is a part of a bigger PR. I tried to extract a meaningful part of it, but there are left over like this one that doesn't make sense.

I'm moving isColorLayerLoaded to LayeredMaterial.

@@ -80,8 +80,7 @@ function executeCommand(command) {
};

geometry._count++;
const tile = new TileMesh(geometry, params);
tile.layer = layer;
const tile = new TileMesh(layer, geometry, params);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question, you remember where tile.layer is used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered above.

} else {
this.setLayerVisibility(index, false);
break;
LayeredMaterial.prototype.setLayerTextures = function setLayerTextures(layer, textures) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you saw @mbredif's proposal for the LayeredMaterial

@@ -371,7 +371,7 @@ function _readZ(layer, method, coord, nodes, cache) {
}

const tile = tileWithValidElevationTexture;
const src = tileWithValidElevationTexture.getLayerTextures(l_ELEVATION)[0];
const src = tileWithValidElevationTexture.material.textures[l_ELEVATION][0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and with the famous tile.layer?
you could write:
tileWithValidElevationTexture.getLayerTextures(tile.layer)[0]

@peppsac
Copy link
Contributor Author

peppsac commented Jun 29, 2018

Updated.

@peppsac
Copy link
Contributor Author

peppsac commented Jul 2, 2018

@gchoqueux could you update your review please?

@peppsac
Copy link
Contributor Author

peppsac commented Jul 5, 2018

Ok, so IMHO this PR improves greatly itowns and will allow to reuse the texture update process for other types of data.

@gchoqueux: I don't think your Request Changes on this PR is justified.

@gchoqueux
Copy link
Contributor

Ok, so IMHO this PR improves greatly itowns and will allow to reuse the texture update process for other types of data

yes, but you also need the missing tests of impacted objects

@peppsac
Copy link
Contributor Author

peppsac commented Jul 10, 2018

yes, but you also need the missing tests of impacted objects

Most of them already have some tests or are pretty well exercised during with the test-examples target. So unless you have a specific test case in mind, I won't add any.

Stop blocking PRs for random reasons: being a reviewer is a responsability, you can't just click Request Changes without valid concerns about the code being merged.

@peppsac peppsac force-pushed the refacto_limit_dependency_on_layeredmaterial branch from 5176049 to 906720d Compare July 10, 2018 15:05
@peppsac
Copy link
Contributor Author

peppsac commented Jul 10, 2018

Rebased.

@peppsac peppsac force-pushed the refacto_limit_dependency_on_layeredmaterial branch from 906720d to 2fb058e Compare July 12, 2018 09:30
Copy link
Contributor

@zarov zarov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a lot of good things here, thanks for this !

@@ -92,11 +92,8 @@
globeView.scene.traverse(function _(obj) {
if (obj.material && obj.material.setLayerVisibility && obj.material.visible) {
material = obj.material;
orthoIndex = material.indexOfColorLayer(orthoLayer.id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove their declaration above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -37,6 +39,7 @@ function TileMesh(geometry, params) {
this.frustumCulled = false;

this.updateGeometricError();
this.wmtsCoords = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not rename it to a more appropriate name in the same time ?

}
} else if (textures.texture !== null) {
this._setTexture(textures.texture,
layer.type == 'color' ? l_COLOR : l_ELEVATION, (slotOffset || 0), textures.pitch);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition unnecessary, result is always l_COLOR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -32,11 +28,10 @@ function TileMesh(geometry, params) {
this.boundingSphere = new THREE.Sphere();
this.OBB().box3D.getBoundingSphere(this.boundingSphere);

this.material = new LayeredMaterial(params.materialOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

geometry._count++;
const tile = new TileMesh(geometry, params);
tile.layer = layer;
const material = new LayeredMaterial(layer.materialOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add somewhere a documentation to what is materialOptions ? Even if it is a quick comment, so we do no forget when we'll generate a better documentation on layers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Note that these options are a workaround to limitations in the current code.

Hopefully these limitations will be removed soon and so will the options :)

LayeredMaterial.prototype.getLayerTextures = function getLayerTextures(layerType, layerId) {
if (layerType === l_ELEVATION) {
LayeredMaterial.prototype.getLayerTextures = function getLayerTextures(layer) {
if (layer.type === 'elevation') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: shouldn't it be more practical to change the constants in LayeredMaterialConstants to elevation, color and all ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, LayeredMaterialConstants should be removed soon because that should be an implementation details.

But we'll also need to have valid layer types as constants somewhere (elevation, color, etc).

@peppsac
Copy link
Contributor Author

peppsac commented Jul 13, 2018

Why not rename it to a more appropriate name in the same time ?

I can't answer inline for this one so I'll reply here: the PR #785 tackles this part, so I limited myself to moving code around.

@peppsac
Copy link
Contributor Author

peppsac commented Jul 13, 2018

@zarov PR updated.

@zarov
Copy link
Contributor

zarov commented Jul 13, 2018

Ok this is looking good to me. One last thing: do you have a suggestion for removing the tile.layer that I don't feel too is really useful (apart from the case you quote) ?

@peppsac
Copy link
Contributor Author

peppsac commented Jul 13, 2018

do you have a suggestion for removing the tile.layer that I don't feel too is really useful (apart from the case you quote) ?

Not really, and I feel it's useful (another use case: in the picking code where we need to filter if an mesh belongs to the requested layer).

Copy link
Contributor

@zarov zarov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok good to go then, squash your last commit please.

Currently the 'level' concept from WMTS has leaked everywhere in iTowns
codebase.

This commits replace its usage in one part of iTowns, by something
equivalent but that doesn't force texture Providers to mock WMTS level
mechanism.
This commits is a step forward stopping to rely on LayeredMaterial
internal representation of textures (using an index and such) in other
parts of iTowns.

This will allow to reuse all this code for objects that don't use a
LayerMaterial instance as their material.
@peppsac peppsac force-pushed the refacto_limit_dependency_on_layeredmaterial branch from a460af0 to 182ba92 Compare July 13, 2018 12:42
@peppsac peppsac merged commit d40385f into master Jul 13, 2018
@peppsac peppsac deleted the refacto_limit_dependency_on_layeredmaterial branch July 13, 2018 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants