Hi, just had a look at Cesium, and a bit more. I like it so far. I come from an Esri world, and one of my favorite tools are CityEngine. I really need to get my CityEngine models in and test in Cesium, and I’ve been stucked for 5 days now. That’s proof my dedication, since I could always use an AarcGIS Ent/Online scene
Spatial references are not my strong site, it alwasy seems to work in Esri, except WGS84, at least CityEgnine is not a fan. CityEnigne exports in multiple formats, however, non are really working with Cesium, at least not GIS layers, with coordnates on it, however, I see that GLB is recommended. I’ve been trying like crazy to convert to 3D tilelayers from EPSG:32632 to EPSG:4326 with an CityEngine Glb exporter. Changing calculations up and down, moveing it around, I think the only place I haven’t hit is Copenhagen, where the model should be placed. How is this done?
I can’t find anything about it. It must be possible? If not, Cesium is probably not the way I should go, then I could just stay with AGOL.
My preferred end result would be that each start shape in CityEngine had their own attribute, an ID in the GLB, so you could ad more functionality to it in JS (don’t even know if that’s possible), but an easy CityEngine → 3D Tiles → Cesium workflow. Even though it’s not working, I’ll still add where I’m at. I hope someone can help with this wotkflow. In my final test I exported the GBL centered (0,0,0) exported a gltf json, with coordinates and changed lon lat
#!/bin/bash
Input og output stier
INPUT_GLB=“$1” # Modtag GLB-filen som første argument
Tjek, at input-filen er angivet og eksisterer
if [ -z “$INPUT_GLB” ] || [ ! -f “$INPUT_GLB” ]; then
echo “Fejl: Du skal angive en gyldig GLB-fil som argument.”
echo “Brug: ./convert.sh <sti_til_glb_fil>”
exit 1
fi
BASE_NAME=$(basename “$INPUT_GLB” .glb)
Vi antager, at den tilhørende GLTF-fil med de originale placeringsdata hedder BASE_NAME.gltf
INPUT_GLTF=“/home/kenneth/3d_converter/Uploads/${BASE_NAME}.gltf”
TEMP_GLTF=“/home/kenneth/3d_converter/Uploads/temp.gltf” # Bruges, hvis INPUT_GLTF ikke findes
FINAL_DIR=“/home/kenneth/portal/backend/static/lib/3D_tiles/$BASE_NAME”
echo “ DEBUG: Starting conversion process…”
echo “Input GLB: $INPUT_GLB”
echo “Base Name: $BASE_NAME”
echo “Input GLTF: $INPUT_GLTF”
echo “Output dir: $FINAL_DIR”
Opret destination og ryd tidligere filer
mkdir -p “$FINAL_DIR”
rm -rf “$FINAL_DIR”/*
Hvis der IKKE findes en GLTF med de originale data, lav en midlertidig med gltf-pipeline
if [ ! -f “$INPUT_GLTF” ]; then
echo “ Eksporterer GLB til GLTF (midlertidig)…”
gltf-pipeline -i “$INPUT_GLB” -o “$TEMP_GLTF”
INPUT_FOR_PY=“$TEMP_GLTF”
else
INPUT_FOR_PY=“$INPUT_GLTF”
fi
Sæt miljøvariable, som Python-scriptet skal bruge
export INPUT_FOR_PY
export BASE_NAME
Generer tileset.json med Python
echo “ Genererer tileset.json…”
python3 - <<‘EOF’
import os, json
from pyproj import Transformer, CRS
from math import sin, cos, sqrt, radians
def geodetic_to_ecef(lon_deg, lat_deg, h):
# WGS84 parametre
a = 6378137.0
f = 1 / 298.257223563
e2 = f * (2 - f)
lon = radians(lon_deg)
lat = radians(lat_deg)
N = a / sqrt(1 - e2 * (sin(lat)**2))
x = (N + h) * cos(lat) * cos(lon)
y = (N + h) * cos(lat) * sin(lon)
z = ((1 - e2) * N + h) * sin(lat)
return x, y, z
def compute_enu_matrix(lon_deg, lat_deg, h):
# Beregn ECEF for center
x, y, z = geodetic_to_ecef(lon_deg, lat_deg, h)
# Konverter til radianer
lon = radians(lon_deg)
lat = radians(lat_deg)
# Udregn east, north og up vektorer
east = [-sin(lon), cos(lon), 0.0]
north = [-sin(lat)*cos(lon), -sin(lat)*sin(lon), cos(lat)]
up = [cos(lat)*cos(lon), cos(lat)*sin(lon), sin(lat)]
# Saml en 4x4 matrix i søjle-major rækkefølge
matrix = [
east[0], north[0], up[0], x,
east[1], north[1], up[1], y,
east[2], north[2], up[2], z,
0.0, 0.0, 0.0, 1.0
]
return matrix
def generate_tileset(gltf_path, output_path, model_name):
# 1. Læs GLTF data med de originale placeringsdata
with open(gltf_path, ‘r’) as f:
gltf_data = json.load(f)
# 2. Udtræk min og max fra første accessor; rækkefølgen er [X, Z, Y]
accessor = gltf_data['accessors'][0]
min_vals = accessor['min'] # [min_x, min_z, min_y]
max_vals = accessor['max'] # [max_x, max_z, max_y]
# Sørg for, at northing (position 2) er positive:
min_north = abs(min_vals[2])
max_north = abs(max_vals[2])
print(f"DEBUG: Model bounds (EPSG:32632):")
print(f" Min: x={min_vals[0]}, y={min_north}, z={min_vals[1]}")
print(f" Max: x={max_vals[0]}, y={max_north}, z={max_vals[1]}")
# 3. Beregn center ud fra dine specifikationer:
# center_x = 2 * min_x - max_x
# center_y = 2 * min_north - max_north
# center_z = min_z
center_x = 2 * min_vals[0] - max_vals[0]
center_y = 2 * min_north - max_north
center_z = min_vals[1]
print(f"DEBUG: Beregnet center (EPSG:32632): x={center_x}, y={center_y}, z={center_z}")
# 4. Transformer center fra EPSG:32632 til WGS84
crs_utm32n = CRS("EPSG:32632")
crs_wgs84 = CRS("EPSG:4326")
transformer = Transformer.from_crs(crs_utm32n, crs_wgs84, always_xy=True)
lon, lat, h = transformer.transform(center_x, center_y, center_z)
print(f"DEBUG: Transformeret center (WGS84): Lon={lon}, Lat={lat}, Height={h}")
# 5. Udregn en ENU transformmatrix (svarende til Cesium.Transforms.eastNorthUpToFixedFrame)
transform_matrix = compute_enu_matrix(lon, lat, h)
print(f"DEBUG: ENU transform matrix: {transform_matrix}")
# 6. Beregn bounding box størrelse (bruges af Cesium til culling)
size = [
abs(max_vals[0] - min_vals[0]), # X-størrelse (easting)
abs(max_north - min_north), # Y-størrelse (northing)
abs(max_vals[1] - min_vals[1]) # Z-størrelse (elevation)
]
# 7. Sammensæt tileset.json strukturen
tileset = {
"asset": {"version": "1.0"},
"geometricError": 200,
"root": {
"transform": transform_matrix,
"boundingVolume": {
"box": [
0, 0, 0,
size[0] / 2, 0, 0,
0, size[1] / 2, 0,
0, 0, size[2] / 2
]
},
"geometricError": 0,
"refine": "ADD",
"content": {
"uri": f"{model_name}.b3dm"
}
}
}
with open(output_path, "w") as f:
json.dump(tileset, f, indent=2)
print(f"✅ Tileset.json genereret i: {output_path}")
if name == “main”:
INPUT_GLTF = os.environ.get(“INPUT_FOR_PY”, “/home/kenneth/3d_converter/Uploads/temp.gltf”)
OUTPUT_DIR = f"/home/kenneth/portal/backend/static/lib/3D_tiles/{os.environ.get(‘BASE_NAME’)}"
MODEL_NAME = os.environ.get(“BASE_NAME”)
generate_tileset(INPUT_GLTF, f"{OUTPUT_DIR}/tileset.json", MODEL_NAME)
EOF
Konverter GLB til B3DM
echo “ Konverterer GLB til B3DM…”
3d-tiles-tools glbToB3dm -i “$INPUT_GLB” -o “$FINAL_DIR/$BASE_NAME.b3dm”
Ryd op midlertidig GLTF, hvis den blev oprettet
if [ -f “$TEMP_GLTF” ]; then
rm “$TEMP_GLTF”
fi
Valider tileset
echo “ Validerer tileset…”
npx ts-node ~/3d-tiles-validator/src/main.ts --tilesetFile “$FINAL_DIR/tileset.json”
echo “ Konvertering færdig. 3D Tiles er gemt i: $FINAL_DIR”
ls -la “$FINAL_DIR”