CesiumGS / cesium-unity

Bringing the 3D geospatial ecosystem to Unity
https://cesium.com/platform/cesium-for-unity/
Apache License 2.0
358 stars 83 forks source link

`file:///` URLs with escaped spaces (`%20`) do not work #522

Open TheYarin opened 1 month ago

TheYarin commented 1 month ago

The CesiumUtility::Uri::resolve() function does not seem to handle a base URI with spaces as expected, even when the spaces are encoded as %20. Specifically, I'm working with a file URI, so let's use file:///C:/Temp/3d%20tiles/tileset.json as an example.

Note: I am not sure the problem is within this specific function, this is just my suspicion. I encountered this issue while trying to load a 3DTiles tileset using Cesium for Unity.

If anyone with a dev environment for cesium-native set up could test that, this would be very helpful. Adding the following code to line 98 of CesiumUtility/test/TestUri.cpp might save you some time:

CHECK(
      CesiumUtility::Uri::resolve("file:///C:/Temp/3d%20tiles", "tiles/1") ==
      "file:///C:/Temp/3d%20tiles/tiles/1");

By the way, for some reason if the base URI is file:///C:/Temp/3dtiles/tileset.json (assuming this path is correct) and the relative path is tiles/1 it still finds the right files somehow, despite ending up with what I expect is a URI to a subfolder of a file, which can't be right. Does anyone have any idea how?

kring commented 1 month ago

Thanks for the test case, but it is invalid. CesiumUtility::Uri::resolve("file:///C:/Temp/3d%20tiles", "tiles/1") should (and does) return file:///C:/Temp/tiles/1. If you want the original URI to be treated as a directory rather than file, add the trailing slash. For example, this works:

  CHECK(
      CesiumUtility::Uri::resolve("file:///C:/Temp/3d%20tiles/", "tiles/1") ==
      "file:///C:/Temp/3d%20tiles/tiles/1");

Think about it like this. In regular old HTML, if you have a web page at: https://example.com/page.html

And inside there you have a link that looks like this: <a href="path/foo.html">Some Link</a>

Where will you go when you click that? The answer is https://example.com/path/foo.html, not https://example.com/page.html/path/foo.html. The answer doesn't change if the original URL is https://example.com/page (that is, if you remove the file extension).

TheYarin commented 1 month ago

@kring How about:

CHECK(
      CesiumUtility::Uri::resolve("file:///C:/Temp/3d%20tiles/layers.json", "1/0/1.ext") ==
      "file:///C:/Temp/3d%20tiles/1/0/1.ext");

compared to:

CHECK(
      CesiumUtility::Uri::resolve("file:///C:/Temp/3d-tiles/layers.json", "1/0/1.ext") ==
      "file:///C:/Temp/3d-tiles/1/0/1.ext");
kring commented 1 month ago

@TheYarin both of those test cases pass.

TheYarin commented 1 month ago

@kring Interesting. Then the problem is probably elsewhere. I recreated the circumstances and the problem indeed reproduces.

Would you mind trying to reproduce the issue I've been encountering? Loading a layer from a layers.json file as a 3DTiles tileset to Cesium for Unity on Windows from a local folder with a space in its name? I can supply the tileset if needed.

kring commented 1 month ago

@TheYarin what problem are you having when you try to do this?

TheYarin commented 4 weeks ago

@kring I'm trying to load a tileset from the following layers.json file located in a path: C:\Temp\3d tiles:

{
  "attribution": "",
  "available": [
    [{ "endX": 1, "endY": 0, "startX": 0, "startY": 0 }],
    [{ "endX": 3, "endY": 1, "startX": 0, "startY": 0 }],
    [{ "endX": 7, "endY": 3, "startX": 0, "startY": 0 }],
    [{ "endX": 15, "endY": 7, "startX": 0, "startY": 0 }],
    [{ "endX": 31, "endY": 15, "startX": 0, "startY": 0 }],
    [{ "endX": 10, "endY": 24, "startX": 10, "startY": 24 }],
    [{ "endX": 20, "endY": 49, "startX": 20, "startY": 49 }],
    [{ "endX": 41, "endY": 98, "startX": 41, "startY": 98 }],
    [{ "endX": 82, "endY": 196, "startX": 82, "startY": 196 }],
    [{ "endX": 164, "endY": 392, "startX": 164, "startY": 392 }],
    [{ "endX": 328, "endY": 785, "startX": 328, "startY": 785 }],
    [{ "endX": 657, "endY": 1571, "startX": 657, "startY": 1571 }],
    [{ "endX": 1315, "endY": 3143, "startX": 1315, "startY": 3142 }],
    [{ "endX": 2631, "endY": 6286, "startX": 2630, "startY": 6285 }],
    [{ "endX": 5262, "endY": 12572, "startX": 5261, "startY": 12571 }],
    [{ "endX": 10524, "endY": 25145, "startX": 10522, "startY": 25143 }],
    [{ "endX": 21049, "endY": 50290, "startX": 21045, "startY": 50286 }],
    [{ "endX": 42099, "endY": 100580, "startX": 42090, "startY": 100573 }],
    [{ "endX": 84199, "endY": 201160, "startX": 84180, "startY": 201147 }]
  ],
  "bounds": [-180, -90, 180, 90],
  "description": "",
  "extensions": ["metadata", "octvertexnormals"],
  "format": "quantized-mesh-1.0",
  "maxzoom": 18,
  "metadataAvailability": 10,
  "minzoom": 0,
  "name": "",
  "projection": "EPSG:4326",
  "scheme": "tms",
  "tiles": ["{z}/{x}/{y}.terrain?v={version}"],
  "version": "1.1.0"
}

When I set the following URL in the Cesium3dTileset: file:///C:/Temp/3d%20tiles/layers.json I get the following error:

 [error] [TilesetContentManager.cpp:997] An unexpected error occurs when loading tile: Request for `https://localhost/0/0/0.terrain?v=1.1.0&extensions=octvertexnormals-metadata` failed: Cannot connect to destination host

UnityEngine.Debug:Log (object)
Reinterop.ReinteropInitializer:UnityEngine_Debug_CallLog_FA05wu8x__otZNsgdHTnU9A (intptr) (at ./Library/PackageCache/com.cesium.unity@1.9.0/Runtime/generated/Reinterop/Reinterop.RoslynSourceGenerator/ReinteropInitializer.cs:60677)
CesiumForUnity.Cesium3DTileset:Update () (at ./Library/PackageCache/com.cesium.unity@1.9.0/Runtime/generated/Reinterop/Reinterop.RoslynSourceGenerator/Cesium3DTileset-generated.cs:606)

And I get the same error if I use a space instead of %20: file:///C:/Temp/3d tiles/layers.json.

If I change the name of the folder to 3d-tiles and update the URL accordingly to file:///C:/Temp/3d-tiles/layers.json, everything works as expected.

It seems that the relative path in the tiles field is handled differently when there's a space compared to when there isn't one.

Where do you think the problem lies?

kring commented 2 weeks ago

This appears to be a bug in Unity. When we construct a UnityWebRequest with the URL file:///C:/Temp/3d%20tiles/layers.json, the url property will end up being file:///C:/Temp/3d tiles/layers.json, which is an invalid URL.

kring commented 2 weeks ago

I'm going to move this issue into the cesium-unity project and update the title.

kring commented 2 weeks ago

There's a similar issue in Unity's issue tracker, but it claims to be fixed: https://issuetracker.unity3d.com/issues/unitywebrequest-dot-url-unescapes-escaped-symbol

And perhaps it was, mostly... %20 seems to work fine in regular https:// URLs, just not in file:/// URLs.

TheYarin commented 2 weeks ago

Hmm, that explains it then. I'm using Unity 2022.3 and still experience this issue.

Did you manage to reproduce the issue on your machine?

kring commented 2 weeks ago

Did you manage to reproduce the issue on your machine?

Yep, I wrote the comment above after reproducing the problem on my system and digging into what went wrong.

TheYarin commented 2 weeks ago

amazing, thank you!