#!/bin/bash # Ubuntu way #apt install xvfb #apt install libxkbcommon0 #apt install blender python3-pip #apt install python3-lxml python3-shapely python3-matplotlib (for CityGML converter) #apt install libxi6 libgconf-2-4 #OR # Debian way #wget https://ftp.halifax.rwth-aachen.de/blender/release/Blender4.4/blender-4.4.3-linux-x64.tar.xz #tar -xvf blender-4.4.3-linux-x64.tar.xz #change .env BLENDER_PATH or make symlink to it `ln -s PATH_TO_YOUR_UNCOMPRESSED_BLENDER/blender-4.4.3-linux64/blender /usr/local/bin/blender` #pip install numpy or apt install python3-numpy #pip install triangle #usage: ./convert.sh -c COMPRESS -cl COMPRESSION_LEVEL -i 'INPUT' -o 'OUTPUT' -b BINARY -f FORCE_OVERRIDE #apt install -y libxi6 libxrender1 libxrandr2 libxinerama1 libxcursor1 libxcomposite1 libxdamage1 libxtst6 libglib2.0-0 libsm6 libice6 libgl1 libxkbcommon0 #TESTING: # sudo blender -b -P ./scripts/2gltf2/2gltf2.py -- --input "/opt/drupal/web/sites/default/files/{NAME}" --ext "$GLTF" --compression "true" --compression_level "3" --output "/opt/drupal/web/sites/default/files/2025-05/test-TEST.glb" # xvfb-run --auto-servernum --server-args="-screen 0 512x512x16" sudo blender -b -P ./scripts/render.py -- --input "/var/www/html/sites/default/files/{NAME}.glb" --ext "glb" --org_ext "glb" --output "/var/www/html/sites/default/files/views/" --is_archive false --resolution 512x512x16 --samples 20 -E BLENDER_EEVEE -f 1 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MODULE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" if [[ -f "$SCRIPT_DIR/.env" ]]; then source "$SCRIPT_DIR/.env" fi BLENDER_BIN="${BLENDER_BIN:-}" if [[ -z "$BLENDER_BIN" ]]; then BLENDER_BIN="blender" fi SPATH="${SPATH:-$MODULE_ROOT}" if [[ ! -d "$SPATH/scripts" ]]; then SPATH="$MODULE_ROOT" fi #Defaults: COMPRESSION=false COMPRESSION_LEVEL=3 GLTF="gltf" FORCE=false isOutput=false IS_ARCHIVE=false LIGHTWEIGHT=false INPUT="" OUTPUT="" OUTPUTPATH="" resolve_blender_bin () { local candidate="$1" if [[ -z "$candidate" ]]; then return 1 fi if [[ "$candidate" == /* ]]; then [[ -f "$candidate" && -x "$candidate" ]] || return 1 printf '%s\n' "$candidate" return 0 fi if [[ -f "$SCRIPT_DIR/$candidate" && -x "$SCRIPT_DIR/$candidate" ]]; then printf '%s\n' "$SCRIPT_DIR/$candidate" return 0 fi if [[ -f "$SPATH/$candidate" && -x "$SPATH/$candidate" ]]; then printf '%s\n' "$SPATH/$candidate" return 0 fi if command -v "$candidate" &> /dev/null; then command -v "$candidate" return 0 fi return 1 } check_blender () { if ! BLENDER_BIN="$(resolve_blender_bin "$BLENDER_BIN")"; then echo "Blender doesn't exist, install it by 'apt install blender python3-pip' then 'pip install numpy' or set BLENDER_BIN in scripts/.env" return 1 elif [[ ! -x "$BLENDER_BIN" ]]; then echo "Configured BLENDER_BIN is not executable: $BLENDER_BIN" return 1 fi if [[ -n "${BLENDER_BIN:-}" ]]; then echo "Blender exists and be used for next steps..." return 0 fi } check_scripts () { if [ ! -d "${SPATH}/scripts" ]; then echo "Can't find dependencies directory. Resolved SPATH=${SPATH}. SCRIPT_DIR=${SCRIPT_DIR}. MODULE_ROOT=${MODULE_ROOT}. Did you change your SPATH value in scripts/.env?" return 1 else return 0 fi } check_blender check_scripts show_usage () { echo "-c=compress -l=compression level -i=input path -o=output path -b=binary -f=force override existing file" cat <&2 exit 1 } bool() { [[ "$1" == "true" || "$1" == "false" ]] || die "Value must be true/false (is: $1)" } file_exists() { [[ -f "$1" ]] || die "File not found: $1" } require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" } ###################################### # Parsing ###################################### while [[ $# -gt 0 ]]; do case "$1" in -c|--compression) bool "$2" COMPRESSION="$2" shift 2 ;; -l|--compression-level) [[ "$2" =~ ^[0-9]$ ]] || die "compression-level must be 0–9" COMPRESSION_LEVEL="$2" shift 2 ;; -i|--input) INPUT="$2" shift 2 ;; -o|--output) OUTPUT="$2" shift 2 ;; -b|--binary) bool "$2" [[ "$2" == "true" ]] && GLTF="glb" || GLTF="gltf" shift 2 ;; -f|--force) bool "$2" FORCE="$2" shift 2 ;; -t|--lightweight) bool "$2" LIGHTWEIGHT="$2" shift 2 ;; -a|--archive) bool "$2" IS_ARCHIVE="$2" shift 2 ;; -h|--help) show_usage exit 0 ;; --) shift break ;; *) die "Error parsing arguments" ;; esac done check_status () { if [ ! -f "$1.off" ]; then touch "$1.off" else rm -rf "$1.off" fi; } cleanup_main_lock() { flock -u 200 2>/dev/null || true exec 200>&- 2>/dev/null || true if [[ -n "${MAIN_LOCKFILE:-}" ]]; then rm -f "$MAIN_LOCKFILE" fi } create_dirs () { mkdir -p "$INPATH"/gltf/ mkdir -p "$INPATH"/metadata/ } create_flock () { INPATH=$1 FILENAME=$2 MAIN_LOCKFILE="$INPATH/${FILENAME}.lock" exec 200>"$MAIN_LOCKFILE" || exit 1 flock -n 200 || { echo "Process already running for $FILENAME" exit 0 } trap cleanup_main_lock EXIT } handle_file () { INPATH=$1 FILENAME=$2 NAME=$3 EXT=$4 OUTPUT=$5 OUTPUTPATH=$6 create_flock "$INPATH" "$FILENAME" if [[ "$isOutput" = false ]]; then "$BLENDER_BIN" -b -P "${SPATH}/scripts/2gltf2/2gltf2.py" -- --input "$INPATH/$FILENAME" --ext "$GLTF" --compression "$COMPRESSION" --compression_level "$COMPRESSION_LEVEL" #> /dev/null 2>&1 else "$BLENDER_BIN" -b -P "${SPATH}/scripts/2gltf2/2gltf2.py" -- --input "$INPATH/$FILENAME" --ext "$GLTF" --compression "$COMPRESSION" --compression_level "$COMPRESSION_LEVEL" --output "$OUTPUT$OUTPUTPATH" #> /dev/null 2>&1 fi } handle_unsupported_file () { INPATH=$1 FILENAME=$2 NAME=$3 EXT=$4 OUTPUT=$5 OUTPUTPATH=$6 #touch "${INPATH}/gltf/${NAME}.glb.off" } handle_ifc_file () { INPATH=$1 FILENAME=$2 NAME=$3 EXT=$4 OUTPUT=$5 OUTPUTPATH=$6 create_flock "$INPATH" "$FILENAME" create_dirs ${SPATH}/scripts/IfcConvert "$INPATH/$FILENAME" "$INPATH/gltf/$NAME.glb" > /dev/null 2>&1 } handle_blend_file () { INPATH=$1 FILENAME=$2 NAME=$3 EXT=$4 create_flock "$INPATH" "$FILENAME" create_dirs "$BLENDER_BIN" -b -P "${SPATH}/scripts/convert-blender-to-gltf.py" -- "$INPATH/$FILENAME" "$INPATH/gltf/$NAME.glb" > /dev/null 2>&1 } handle_gml_file () { INPATH=$1 FILENAME=$2 NAME=$3 EXT=$4 OUTPUT=$5 OUTPUTPATH=$6 create_flock "$INPATH" "$FILENAME" require_cmd python3 GLB_PATH="${INPATH}/${NAME}_GLB" mkdir -p "$GLB_PATH" cp -rf "$INPATH/$FILENAME" "$GLB_PATH/" python3 "${SPATH}/scripts/CityGML2OBJv2/CityGML2OBJs.py" -i "$GLB_PATH" -o "$GLB_PATH" > /dev/null 2>&1 create_dirs "$BLENDER_BIN" -b -P "${SPATH}/scripts/2gltf2/2gltf2.py" -- "$GLB_PATH/${NAME}.obj" "$GLTF" "$COMPRESSION" "$COMPRESSION_LEVEL" "$INPATH/gltf/$NAME.glb" > /dev/null 2>&1 rm -rf "$GLB_PATH" } printf "\n" echo "======== Parameters ========" echo " INPUT: $INPUT" echo " OUTPUT: $OUTPUT" echo " COMPRESSION: $COMPRESSION" echo " LEVEL: $COMPRESSION_LEVEL" echo " FORMAT: $GLTF" echo " FORCE: $FORCE" echo " LIGHTWEIGHT: $LIGHTWEIGHT" echo " ARCHIVE: $IS_ARCHIVE" echo "===========================" printf "\n" if [[ ! -z "$INPUT" && -f $INPUT ]]; then FILENAME=${INPUT##*/} NAME="${FILENAME%.*}" EXT=${FILENAME//*.} INPATH=${INPUT%/*} if [[ $FILENAME = $INPATH ]]; then INPATH="." fi if [[ -z "$OUTPUT" ]]; then OUTPUT=`echo ${INPATH}/gltf` else #echo $OUTPUT OUTFILENAME=${OUTPUT%/*} # trim everything past the last / OUTFILENAME=${OUTFILENAME##*/} OUTFILENAME=$(printf '%s' "$OUTFILENAME" | sed -E 's/_(ZIP|RAR|TAR|XZ|GZ)$//I') OUTPUTPATH=`echo $OUTFILENAME.$GLTF` OUTPUT=`echo ${OUTPUT}gltf/` isOutput=true fi if [[ "$EXT" != "$FILENAME" ]]; then EXT="${EXT,,}" if [[ ! -d "$OUTPUT" ]]; then mkdir "$OUTPUT" fi if [[ ! -f $OUTPUT/$NAME.$GLTF || $FORCE ]]; then start=`date +%s` case $EXT in abc|dae|fbx|obj|ply|stl|wrl|x3d) echo "Converting $EXT file..." handle_file "$INPATH" "$FILENAME" "$NAME" $EXT "$OUTPUT" "$OUTPUTPATH" end=`date +%s` echo "File $FILENAME compressed successfully. Runtime: $((end-start))s." exit 0; ;; ifc) echo "Converting $EXT file..." handle_ifc_file "$INPATH" "$FILENAME" "$NAME" $EXT "$OUTPUT" "$OUTPUTPATH" end=`date +%s` echo "File $FILENAME compressed successfully. Runtime: $((end-start))s." exit 0; ;; blend) echo "Converting $EXT file..." handle_blend_file "$INPATH" "$FILENAME" "$NAME" $EXT end=`date +%s` echo "File $FILENAME compressed successfully. Runtime: $((end-start))s." exit 0; ;; gml) echo "Converting $EXT file..." handle_gml_file "$INPATH" "$FILENAME" "$NAME" $EXT "$OUTPUT" "$OUTPUTPATH" end=`date +%s` echo "File $FILENAME compressed successfully. Runtime: $((end-start))s." exit 0; ;; glb) end=`date +%s` echo "Given file was already compressed." exit 0; ;; *) handle_unsupported_file "$INPATH" "$FILENAME" "$NAME" $EXT "$OUTPUT" "$OUTPUTPATH" #echo "File extension $EXT is not supported for conversion yet." exit 0; ;; esac else echo "Compressed file $OUTPUT/$NAME.$GLTF already exists." exit 1; fi else echo "No extension found on $FILENAME"; exit 2; fi elif [[ -z "$INPUT" ]] && die "No --input argument provided"; then file_exists "$INPUT" elif [[ -n "$OUTPUT" && -f "$OUTPUT" && "$FORCE" != "true" ]]; then die "Output file already exists (use --force true)" else echo "Given file '$INPUT' not found" show_usage fi