add per-tile min/max elevation support

This commit is contained in:
2026-01-23 16:42:17 +01:00
parent 04ef2c68cc
commit bdbd0eb99b
5 changed files with 111 additions and 42 deletions

View File

@@ -69,6 +69,7 @@ public class GeoTilePrefabImporter : EditorWindow
public int YKey;
public double Xmin, Ymin, Xmax, Ymax;
public double GlobalMin, GlobalMax;
public double TileMin, TileMax;
public int OutRes;
}
@@ -144,6 +145,7 @@ public class GeoTilePrefabImporter : EditorWindow
"Creates one .prefab asset per tile in the manifest.\n" +
"Each prefab contains: Terrain (with TerrainData), Buildings, Trees, Furniture.\n" +
"TerrainData and TerrainLayers are saved as separate .asset files.\n" +
"Per-tile elevation: uses tile_min/tile_max when present (falls back to global_min/global_max).\n" +
"IMPORTANT: Use 'Place All Prefabs in Scene' to position tiles correctly.",
MessageType.Info);
@@ -495,6 +497,8 @@ public class GeoTilePrefabImporter : EditorWindow
int IDX_YMAX = headerMap["ymax"];
int IDX_GMIN = headerMap["global_min"];
int IDX_GMAX = headerMap["global_max"];
int IDX_TMIN = headerMap.ContainsKey("tile_min") ? headerMap["tile_min"] : -1;
int IDX_TMAX = headerMap.ContainsKey("tile_max") ? headerMap["tile_max"] : -1;
int IDX_RES = headerMap["out_res"];
int IDX_TILE_KEY = headerMap.ContainsKey("tile_key") ? headerMap["tile_key"] : -1;
@@ -509,6 +513,10 @@ public class GeoTilePrefabImporter : EditorWindow
Math.Max(Math.Max(IDX_YMAX, IDX_GMIN), Math.Max(IDX_GMAX, IDX_RES)));
if (IDX_TILE_KEY >= 0)
maxIdx = Math.Max(maxIdx, IDX_TILE_KEY);
if (IDX_TMIN >= 0)
maxIdx = Math.Max(maxIdx, IDX_TMIN);
if (IDX_TMAX >= 0)
maxIdx = Math.Max(maxIdx, IDX_TMAX);
if (parts.Length <= maxIdx)
{
Debug.LogWarning($"[GeoTilePrefabImporter] Skipping line {i + 1}: too few columns.");
@@ -521,6 +529,16 @@ public class GeoTilePrefabImporter : EditorWindow
var ymin = double.Parse(parts[IDX_YMIN], ci);
var tileKeyRaw = IDX_TILE_KEY >= 0 ? parts[IDX_TILE_KEY].Trim() : "";
var tileKey = ResolveTileKey(tileKeyRaw, xmin, ymin, out var xKey, out var yKey);
var globalMin = double.Parse(parts[IDX_GMIN], ci);
var globalMax = double.Parse(parts[IDX_GMAX], ci);
double tileMin = globalMin;
double tileMax = globalMax;
if (IDX_TMIN >= 0 && IDX_TMIN < parts.Length &&
double.TryParse(parts[IDX_TMIN], NumberStyles.Float, ci, out var parsedTileMin))
tileMin = parsedTileMin;
if (IDX_TMAX >= 0 && IDX_TMAX < parts.Length &&
double.TryParse(parts[IDX_TMAX], NumberStyles.Float, ci, out var parsedTileMax))
tileMax = parsedTileMax;
tiles.Add(new TileMetadata
{
@@ -532,8 +550,10 @@ public class GeoTilePrefabImporter : EditorWindow
Ymin = ymin,
Xmax = double.Parse(parts[IDX_XMAX], ci),
Ymax = double.Parse(parts[IDX_YMAX], ci),
GlobalMin = double.Parse(parts[IDX_GMIN], ci),
GlobalMax = double.Parse(parts[IDX_GMAX], ci),
GlobalMin = globalMin,
GlobalMax = globalMax,
TileMin = tileMin,
TileMax = tileMax,
OutRes = int.Parse(parts[IDX_RES], ci)
});
}
@@ -601,7 +621,7 @@ public class GeoTilePrefabImporter : EditorWindow
private bool CreateTilePrefab(TileMetadata tile)
{
// Validate height range
float heightRange = (float)(tile.GlobalMax - tile.GlobalMin);
float heightRange = (float)(tile.TileMax - tile.TileMin);
if (heightRange <= 0.0001f)
{
Debug.LogWarning($"[GeoTilePrefabImporter] Tile {tile.TileId}: invalid height range. Skipping.");
@@ -686,6 +706,8 @@ public class GeoTilePrefabImporter : EditorWindow
metadata.ymin = tile.Ymin;
metadata.globalMin = tile.GlobalMin;
metadata.globalMax = tile.GlobalMax;
metadata.tileMin = tile.TileMin;
metadata.tileMax = tile.TileMax;
// Add child components
if (includeBuildings)
@@ -769,15 +791,15 @@ public class GeoTilePrefabImporter : EditorWindow
}
// Building GLB vertices have absolute Z (elevation) from CityGML.
// Since prefab root will be at Y=gmin when placed, offset buildings by -gmin
// so building world Y = gmin + (-gmin) + GLB_Y = GLB_Y (correct absolute elevation)
// Since prefab root will be at Y=tile_min when placed, offset buildings by -tile_min
// so building world Y = tile_min + (-tile_min) + GLB_Y = GLB_Y (correct absolute elevation)
var instance = PrefabUtility.InstantiatePrefab(buildingPrefab) as GameObject;
if (instance == null)
instance = Instantiate(buildingPrefab);
instance.name = "Buildings";
instance.transform.SetParent(root.transform, false);
instance.transform.localPosition = new Vector3(0f, -(float)tile.GlobalMin, 0f);
instance.transform.localPosition = new Vector3(0f, -(float)tile.TileMin, 0f);
instance.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
instance.isStatic = true;
}
@@ -799,11 +821,11 @@ public class GeoTilePrefabImporter : EditorWindow
}
// Tree GLB vertices use absolute elevation (z_ground from DGM).
// Since prefab root will be at Y=gmin when placed, offset trees by -gmin
// so tree world Y = gmin + (-gmin) + GLB_Y = GLB_Y (correct absolute elevation)
// Since prefab root will be at Y=tile_min when placed, offset trees by -tile_min
// so tree world Y = tile_min + (-tile_min) + GLB_Y = GLB_Y (correct absolute elevation)
var treesContainer = new GameObject("Trees");
treesContainer.transform.SetParent(root.transform, false);
treesContainer.transform.localPosition = new Vector3(0f, -(float)tile.GlobalMin, 0f);
treesContainer.transform.localPosition = new Vector3(0f, -(float)tile.TileMin, 0f);
treesContainer.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
treesContainer.isStatic = true;
@@ -863,7 +885,7 @@ public class GeoTilePrefabImporter : EditorWindow
furnitureContainer.transform.localPosition = Vector3.zero;
furnitureContainer.isStatic = true;
float gmin = (float)tile.GlobalMin;
float baseY = (float)tile.TileMin;
for (int i = 1; i < lines.Length; i++)
{
@@ -883,7 +905,7 @@ public class GeoTilePrefabImporter : EditorWindow
GameObject obj = CreateFurnitureObject(furnitureType, height, i);
obj.transform.SetParent(furnitureContainer.transform, false);
obj.transform.localPosition = new Vector3(xLocal, zGround - gmin, yLocal);
obj.transform.localPosition = new Vector3(xLocal, zGround - baseY, yLocal);
obj.isStatic = true;
}
catch (Exception e)
@@ -978,7 +1000,7 @@ public class GeoTilePrefabImporter : EditorWindow
treesContainer.transform.localPosition = Vector3.zero;
treesContainer.isStatic = true;
float gmin = (float)tile.GlobalMin;
float baseY = (float)tile.TileMin;
for (int i = 1; i < lines.Length; i++)
{
@@ -1008,7 +1030,7 @@ public class GeoTilePrefabImporter : EditorWindow
GameObject treeObj = CreateEnhancedTree(height, radius, canopyColor, i);
treeObj.transform.SetParent(treesContainer.transform, false);
treeObj.transform.localPosition = new Vector3(xLocal, zGround - gmin, yLocal);
treeObj.transform.localPosition = new Vector3(xLocal, zGround - baseY, yLocal);
treeObj.isStatic = true;
}
catch (Exception e)