Files
GeoData/scripts/run_citygml_to_glb.sh

139 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# End-to-end helper to convert CityGML tiles to tile-local GLBs.
# Assumes heightmaps/textures are already exported so tile_index.csv exists.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
RAW_DIR="${RAW_DIR:-$ROOT/raw/citygml/lod2}"
CITYJSON_DIR="${CITYJSON_DIR:-$ROOT/work/cityjson}"
CLEAN_DIR="${CLEAN_DIR:-$ROOT/work/cityjson_clean}"
TRI_DIR="${TRI_DIR:-$ROOT/work/cityjson_tri}"
TRI_LOCAL_DIR="${TRI_LOCAL_DIR:-$ROOT/work/cityjson_tri_local}"
SPLIT_DIR="${SPLIT_DIR:-$ROOT/work/cityjson_split_local}"
GLB_DIR="${GLB_DIR:-$ROOT/export_unity/buildings_glb}"
GLB_SPLIT_DIR="${GLB_SPLIT_DIR:-$ROOT/export_unity/buildings_glb_split}"
TILE_INDEX="${TILE_INDEX:-$ROOT/export_unity/tile_index.csv}"
CITYGML_TOOLS="${CITYGML_TOOLS:-$ROOT/tools/citygml-tools-2.4.0/citygml-tools}"
CJIO_CMD="${CJIO:-uv run cjio}"
PYTHON_CMD="${PYTHON_CMD:-uv run python}"
log() {
printf '[citygml->glb] %s\n' "$*"
}
resolve_cityjson() {
local path="$1"
if [[ -f "$path" ]]; then
echo "$path"
return 0
fi
if [[ -d "$path" ]]; then
local base
base="$(basename "$path" .city.json)"
if [[ -f "$path/$base.json" ]]; then
echo "$path/$base.json"
return 0
fi
local first
first="$(find "$path" -maxdepth 1 -name '*.json' | head -n1 || true)"
if [[ -n "$first" ]]; then
echo "$first"
return 0
fi
fi
return 1
}
if [[ ! -f "$TILE_INDEX" ]]; then
echo "tile_index.csv missing at $TILE_INDEX; run heightmap export first." >&2
exit 1
fi
mapfile -t GMLS < <(find "$RAW_DIR" -maxdepth 1 -name 'LoD2_*.gml' | sort)
if [[ ${#GMLS[@]} -eq 0 ]]; then
echo "No CityGML tiles found in $RAW_DIR" >&2
exit 1
fi
mkdir -p "$CITYJSON_DIR" "$CLEAN_DIR" "$TRI_DIR" "$TRI_LOCAL_DIR" "$SPLIT_DIR" "$GLB_DIR" "$GLB_SPLIT_DIR"
log "CityGML -> CityJSON (${#GMLS[@]} tiles)"
for gml in "${GMLS[@]}"; do
base="$(basename "$gml" .gml)"
out="$CITYJSON_DIR/${base}.city.json"
if [[ -e "$out" ]]; then
log "skip existing $out"
continue
fi
"$CITYGML_TOOLS" to-cityjson "$gml" -o "$out"
done
log "Clean invalid vertices"
$PYTHON_CMD "$ROOT/scripts/clean_cityjson_vertices.py" --input-dir "$CITYJSON_DIR" --output-dir "$CLEAN_DIR"
log "Triangulate"
mapfile -t CITYJSONS < <(find "$CLEAN_DIR" -maxdepth 1 -name '*.city.json' | sort)
for path in "${CITYJSONS[@]}"; do
base="$(basename "$path" .city.json)"
target="$TRI_DIR/${base}.tri.city.json"
if [[ -e "$target" ]]; then
log "skip existing $target"
continue
fi
input_json="$(resolve_cityjson "$path" || true)"
if [[ -z "$input_json" ]]; then
log "skip $path (cannot resolve json)"
continue
fi
$CJIO_CMD --ignore_duplicate_keys "$input_json" upgrade triangulate vertices_clean save "$target"
done
log "Rebase to tile-local XY"
$PYTHON_CMD "$ROOT/scripts/rebase_cityjson_tiles.py" --input-dir "$TRI_DIR" --output-dir "$TRI_LOCAL_DIR" --tile-index "$TILE_INDEX"
log "Split semantics (roof/wall)"
$PYTHON_CMD "$ROOT/scripts/split_semantics.py" --input-dir "$TRI_LOCAL_DIR" --output-dir "$SPLIT_DIR"
log "Export GLB (base)"
mapfile -t TRIS < <(find "$TRI_LOCAL_DIR" -maxdepth 1 -name '*.tri.city.json' | sort)
for tri in "${TRIS[@]}"; do
base="$(basename "$tri" .tri.city.json)"
out="$GLB_DIR/${base}.glb"
if [[ -e "$out" ]]; then
log "skip existing $out"
continue
fi
$CJIO_CMD "$tri" export glb "$out"
done
log "Export GLB (roof/wall)"
mapfile -t ROOFS < <(find "$SPLIT_DIR" -maxdepth 1 -name '*.roof.city.json' | sort)
for roof in "${ROOFS[@]}"; do
base="$(basename "$roof" .roof.city.json)"
out="$GLB_SPLIT_DIR/${base}_roof.glb"
if [[ -e "$out" ]]; then
log "skip existing $out"
continue
fi
$CJIO_CMD "$roof" export glb "$out"
done
mapfile -t WALLS < <(find "$SPLIT_DIR" -maxdepth 1 -name '*.wall.city.json' | sort)
for wall in "${WALLS[@]}"; do
base="$(basename "$wall" .wall.city.json)"
out="$GLB_SPLIT_DIR/${base}_wall.glb"
if [[ -e "$out" ]]; then
log "skip existing $out"
continue
fi
$CJIO_CMD "$wall" export glb "$out"
done
log "Verify outputs"
$PYTHON_CMD "$ROOT/scripts/verify_pipeline.py" --mode both --tri-dir "$TRI_LOCAL_DIR" --split-dir "$SPLIT_DIR"
log "Done"