Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Build and push Docker image

on:
push:
branches: [skp2blend]
paths:
- "skp2blend/**"
- ".github/workflows/docker.yml"
workflow_dispatch:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/skp2blend
SDK_RELEASE_URL: https://github.com/RedHaloStudio/Sketchup_Importer/releases/download/0.27.0/sketchup_importer-0.27.zip

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Download SketchUp SDK from upstream release
run: |
wget -qO /tmp/sdk.zip "$SDK_RELEASE_URL"
unzip -q /tmp/sdk.zip -d /tmp/sdk

# Place DLLs where the Dockerfile expects them
mkdir -p skp2blend/sketchup_sdk/binaries/sketchup/x64
cp /tmp/sdk/sketchup_importer/SketchUpAPI.dll \
/tmp/sdk/sketchup_importer/SketchUpCommonPreferences.dll \
skp2blend/sketchup_sdk/binaries/sketchup/x64/

# Place the Python 3.11 compiled extension
cp /tmp/sdk/sketchup_importer/sketchup.cp311-win_amd64.pyd \
skp2blend/sketchup.pyd

rm -rf /tmp/sdk /tmp/sdk.zip

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=raw,value=latest

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: skp2blend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
2 changes: 2 additions & 0 deletions skp2blend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__
*.pyc
76 changes: 76 additions & 0 deletions skp2blend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# ===========================================================================
# Two-stage SKP-to-Blend converter — Docker image
#
# Build-time prerequisites (not redistributable — user must supply):
# sketchup_sdk/ SketchUp C SDK for Windows x64
# binaries/sketchup/x64/SketchUpAPI.dll (+ companion DLLs)
# sketchup.pyd Compiled Cython extension for Windows Python 3.11 x64
# (built via: python setup.py build_ext --inplace)
#
# Build:
# docker build -t skp2blend .
#
# Run:
# docker run --rm -v /path/to/files:/data skp2blend /data/model.skp /data/model.blend
# ===========================================================================

FROM scottyhardy/docker-wine:stable-11.0

USER root
ENV DEBIAN_FRONTEND=noninteractive

# ── Extra packages not in the base image ─────────────────────────────────
RUN apt-get update && \
apt-get install -y --no-install-recommends \
unzip xz-utils \
libxi6 libxrender1 libxfixes3 libglib2.0-0 libxkbcommon0 \
libgl1 libsm6 \
&& rm -rf /var/lib/apt/lists/*

# ── Wine prefix init ────────────────────────────────────────────────────
ENV WINEDEBUG=-all
ENV WINEPREFIX=/root/.wine
ENV WINEARCH=win64

RUN xvfb-run -a wine wineboot --init && wineserver -w

# ── Windows Python 3.11 (embeddable) ───────────────────────────────────
RUN wget -qO /tmp/python-embed.zip \
"https://www.python.org/ftp/python/3.11.9/python-3.11.9-embed-amd64.zip" && \
mkdir -p "${WINEPREFIX}/drive_c/Python311" && \
cd "${WINEPREFIX}/drive_c/Python311" && \
unzip /tmp/python-embed.zip && \
rm /tmp/python-embed.zip

# Enable import of .py files next to the embedded Python
RUN sed -i 's/^#import site/import site/' \
"${WINEPREFIX}/drive_c/Python311/python311._pth"

# ── SketchUp SDK + compiled extension ──────────────────────────────────
COPY sketchup_sdk/binaries/sketchup/x64/*.dll "${WINEPREFIX}/drive_c/Python311/"
COPY sketchup.pyd "${WINEPREFIX}/drive_c/Python311/"

# ── Blender (headless) ─────────────────────────────────────────────────
ARG BLENDER_VERSION=5.0.1
ARG BLENDER_URL=https://download.blender.org/release/Blender5.0/blender-5.0.1-linux-x64.tar.xz
RUN wget -qO /tmp/blender.tar.xz "${BLENDER_URL}" && \
mkdir -p /opt/blender && \
tar -xf /tmp/blender.tar.xz -C /opt/blender --strip-components=1 && \
rm /tmp/blender.tar.xz && \
ln -s /opt/blender/blender /usr/local/bin/blender

# ── Converter scripts ──────────────────────────────────────────────────
COPY intermediate.py skputil.py skp_extractor.py blend_builder.py obj_builder.py \
render_preview.py cli.py \
/opt/skp2blend/

# Also copy the Python modules into the Wine Python directory so the
# extractor can import them
RUN cp /opt/skp2blend/intermediate.py /opt/skp2blend/skputil.py \
"${WINEPREFIX}/drive_c/Python311/"

ENV PATH="/opt/skp2blend:${PATH}"

ENTRYPOINT ["python3", "/opt/skp2blend/cli.py", \
"--blender", "/usr/local/bin/blender", \
"--wine-python", "C:\\Python311\\python.exe"]
4 changes: 4 additions & 0 deletions skp2blend/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#! make

build:
docker build -t skp2blend .
147 changes: 147 additions & 0 deletions skp2blend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# skp2blend

Convert SketchUp `.skp` files to Blender `.blend` (and optionally Wavefront `.obj`) on Linux amd64 — no SketchUp installation required.

## Motivation

The SketchUp C SDK and its Python bindings (`sketchup.pyd`) are Windows-only. This makes batch-converting `.skp` files on Linux servers or CI pipelines impossible without a Windows machine. skp2blend solves this by packaging everything into a single Docker image:

- **Wine** runs the Windows Python interpreter and SketchUp SDK to read `.skp` files
- **Blender headless** builds the `.blend` output with full material, texture, camera, and hierarchy support
- **Pure-Python OBJ export** provides a lightweight alternative output format with no Blender dependency

The result is a self-contained CLI tool that runs on any Linux amd64 host with Docker.

## Quick start

```bash
# Using the convenience wrapper (builds/pulls the Docker image as "skp2blend")
./convert.sh model.skp model.blend

# Produce both .blend and .obj
./convert.sh model.skp model.blend --also-obj

# OBJ only (faster — skips Blender entirely)
./convert.sh model.skp model.blend --obj-only
```

Or run the Docker image directly:

```bash
docker run --rm \
-v "$(pwd):/data" \
skp2blend \
/data/model.skp /data/model.blend
```

## How it works

Conversion runs in two stages:

1. **Stage 1 — Extract** (`skp_extractor.py`, runs under Wine)
Reads the `.skp` file via the SketchUp C SDK and writes a portable `intermediate.json` plus extracted texture files to a work directory.

2. **Stage 2 — Build .blend** (`blend_builder.py`, runs inside Blender headless)
Reads `intermediate.json` and constructs the Blender scene: materials with Principled BSDF nodes, UV-mapped textures, cameras, the full group/component hierarchy, and deduplicated instancing for repeated components.

3. **Stage 2b — Build .obj** (`obj_builder.py`, pure Python, optional)
Reads the same `intermediate.json`, flattens the entity tree into world-space geometry, and writes `.obj` + `.mtl` files with texture references. Useful for side-by-side comparison with the `.blend` output or as an archival format.

## CLI options

| Flag | Description |
|---|---|
| `--scene NAME` | Import a specific named SketchUp scene (applies layer visibility and camera) |
| `--max-instance N` | Instancing threshold — components appearing N+ times are deduplicated (default: 1) |
| `--clip-end F` | Camera far clip plane in meters (default: 250.0) |
| `--preview` | Render a 1920×1080 PNG preview image next to the output `.blend` |
| `--also-obj` | Also produce a `.obj` file alongside the `.blend` |
| `--obj-only` | Only produce `.obj` output, skip Blender |
| `--keep-work-dir` | Retain the intermediate work directory after conversion |
| `--work-dir PATH` | Use a specific work directory instead of a temporary one |

## Using the GHCR image

Pre-built images are published to GHCR by CI on every push to the `skp2blend` branch:

```bash
docker run --rm \
-v "$(pwd):/data" \
ghcr.io/recraft-ou/sketchup_importer/skp2blend:latest \
/data/model.skp /data/model.blend --preview
```

### GPU-accelerated rendering

Pass your GPU to the container for faster EEVEE preview renders. Without a GPU, Blender falls back to software rendering (works but slower).

**NVIDIA** (requires [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)):

```bash
docker run --rm --gpus all \
-v "$(pwd):/data" \
ghcr.io/recraft-ou/sketchup_importer/skp2blend:latest \
/data/model.skp /data/model.blend --preview
```

**AMD / Intel** (Mesa/RADV — pass the DRI render nodes):

```bash
docker run --rm --device /dev/dri \
-v "$(pwd):/data" \
ghcr.io/recraft-ou/sketchup_importer/skp2blend:latest \
/data/model.skp /data/model.blend --preview
```

## Building the Docker image

The image requires the SketchUp C SDK DLLs and a compiled `sketchup.pyd`, which are not included in this repository. They can be obtained from the [upstream release](https://github.com/RedHaloStudio/Sketchup_Importer/releases/tag/0.27.0):

```bash
# Download and extract the SDK artifacts
wget -qO /tmp/sdk.zip \
https://github.com/RedHaloStudio/Sketchup_Importer/releases/download/0.27.0/sketchup_importer-0.27.zip
unzip -q /tmp/sdk.zip -d /tmp/sdk

# Place them where the Dockerfile expects
mkdir -p sketchup_sdk/binaries/sketchup/x64
cp /tmp/sdk/sketchup_importer/SketchUpAPI.dll \
/tmp/sdk/sketchup_importer/SketchUpCommonPreferences.dll \
sketchup_sdk/binaries/sketchup/x64/
cp /tmp/sdk/sketchup_importer/sketchup.cp311-win_amd64.pyd sketchup.pyd

# Build
docker build -t skp2blend .
```

A GitHub Actions workflow (`.github/workflows/docker.yml`) automates this and pushes the image to GHCR on every push to the `skp2blend` branch.

## Output formats

### .blend

Full-fidelity Blender scene with:
- Principled BSDF materials with packed textures
- UV mapping
- Group/component hierarchy preserved as Blender parent-child relationships
- Named cameras from SketchUp scenes
- VERTS-based instancing for repeated components
- Negative-scale correction for mirrored components

### .obj + .mtl

Flat geometry suitable for interchange and archival:
- All transforms baked into world-space vertex positions
- Z-up to Y-up coordinate conversion (OBJ convention)
- Material colors, opacity, and texture map references in `.mtl`
- Texture files copied to a `textures/` directory alongside the `.obj`
- No hierarchy, cameras, or instancing (OBJ limitation)

## Exit codes

| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Bad input (missing file, bad arguments) |
| 2 | Stage 1 failure (extractor) |
| 3 | Stage 2 or 2b failure (builder) |
Loading