Addon Architecture¶
How blender_skp_io converts between a skppy model and a Blender scene.
Module overview¶
blender_skp_io/blender_skp_io/
+- __init__.py # Registration and bl_info
+- annotation_builder.py# Text and dimension conversion
+- blender_manifest.toml# Extension manifest (version, Blender minimum)
+- export_builder.py # BlenderModelBuilder - Blender to public Model
+- scene_builder.py # BlenderSceneBuilder - main conversion logic
\- operators/
+- export_skp.py # EXPORT_OT_skp operator + export properties
+- import_skp.py # IMPORT_OT_skp operator + property declarations
Import pipeline¶
When the user clicks Import SketchUp:
IMPORT_OT_skp(operators/import_skp.py)Imports started through Blender’s file browser run
skppy.load(filepath)in a worker thread. The operator remains modal while parsing, so Blender’s event loop, redraws, and status-bar progress remain responsive.Only parsing runs in the worker. All
bpyaccess and scene construction stay on Blender’s main thread.Direct Python calls such as
bpy.ops.import_scene.skp(...)retain synchronousexecute()semantics and return only after the scene is available. Background-mode Blender also uses this synchronous path.The library detects the container automatically: ZIP/VFF files use the modern parser and pre-ZIP CArchive files use
parser_legacy. The Blender operator does not expose or require a format selector.Instantiates
BlenderSceneBuilderwith all import options.Calls
builder.build().
The status bar shows parsing time while the worker is active. Once parsing
finishes, BlenderSceneBuilder reports normalized progress for collections,
materials, definitions, root geometry, instances, cameras, and finalization.
Mesh and datablock creation must remain on Blender’s main thread; individual
large mesh operations can therefore still pause interaction briefly, but the
long parser phase no longer blocks the UI.
The release ZIP contains the complete skppy package, including
skppy/parser_legacy. Development installs must likewise place or symlink the
library inside blender_skp_io/skppy; otherwise the addon’s relative import
cannot resolve either parser.
Export pipeline¶
When the user clicks Export SketchUp, EXPORT_OT_skp creates a
BlenderModelBuilder with the file-browser settings. The builder:
Resolves the selected, visible, or complete scene object scope.
Creates SketchUp tags from collections and materials from Principled BSDF state, including packed or file-backed texture images.
Converts Blender mesh data to shared SKP component definitions. Repeated unmodified mesh datablocks reuse one definition; evaluated modifier results receive independent definitions when necessary.
Places objects as root instances with inch-scaled world transforms and maps collection-instance Empties to definitions containing nested instances. Collection members use parent-relative transforms and the collection’s instance offset, and child definitions are emitted before their container.
Converts Font objects, cameras, timeline camera markers, and scalar custom properties to text annotations, camera/scene records, and attribute dictionaries.
Calls
Model.save(), which validates and assembles the complete container before replacing the destination.
Mesh conversion keeps one SKP vertex and edge per Blender mesh element and builds directed face loops from Blender loop edge indices. Active per-loop UVs are fitted to each face’s projective SKP mapping, with exact control points for the first four corners. A retained edge receives SketchUp’s soft and smooth flags only when it has exactly two adjacent polygons, both polygons are smooth-shaded, and the Blender edge is not explicitly sharp. Boundary, non-manifold, sharp, and smooth/flat-transition edges remain hard.
Collection-instance conversion does not depend on evaluated matrix_world
values for objects that live only in an unlinked source collection. It composes
each member’s matrix_parent_inverse @ matrix_basis through the parent chain,
subtracts Collection.instance_offset, and deduplicates objects by Blender
datablock identity when they are linked through multiple collection paths.
Definitions are serialized dependency-first because official SketchUp readers
must encounter child mesh or collection definitions before a containing
definition references them. Parent and collection cycles are reported as
conversion errors. Unsupported Blender object families are counted and
reported by the operator.
BlenderSceneBuilder.build()(scene_builder.py)Creates a top-level import collection named after the file.
Calls each build step in order:
_build_layer_collections()_build_materials()_build_definitions()_build_root_geometry()_build_root_construction()_build_root_annotations()_build_root_instances()_build_cameras()
When collection instances are enabled, reachable definition/material variants are built once and nested component references reuse those unlinked source collections. The default, layer, and flattened imports use expanded objects instead.
Calls the optional progress callback between phases and after each material and component definition.
Layer collections¶
_build_layer_collections() runs only when import_by_layers=True.
One Blender collection per
Layerobject.col.hide_viewport = not layer.visible.Collections are stored in a dict keyed by
layer.id.Root faces are split into one mesh object per layer and linked to that collection. Untagged faces remain in the top-level import collection.
Component, group, and image objects use their own layer collection; nested objects without an explicit layer remain with their parent’s collection.
Guide points, guide lines, and section-plane helpers follow the same layer assignment, including when they belong to a component definition.
Construction entities¶
_build_root_construction() and _build_construction_entities() preserve
non-rendering SketchUp construction geometry as Blender viewport helpers:
Guide points become spherical Empty objects at their stored positions.
Guide lines become non-rendering mesh edges. Because Blender has no infinite line primitive, the viewport representation extends 1000 SketchUp inches in each direction from the stored point.
Section planes become cube-display Empty objects whose local Z axis matches the plane normal. Their source plane equation and optional symbol are kept in custom properties.
Helpers inside component definitions inherit the same hierarchy and transforms as definition geometry. In flat-hierarchy mode, their transforms are accumulated into world space. A section-plane helper preserves source data but does not activate Blender clipping, whose behavior and scope differ from SketchUp’s.
Text and dimensions¶
BlenderAnnotationBuilder converts the shared, container-independent
annotation classes into Blender objects:
Text labels become Font objects, with a mesh segment for a visible leader.
Linear dimensions use a Font object plus mesh segments for the dimension and extension lines.
Radial dimensions use a Font object and add a radial segment when the shared model contains enough placement geometry.
Annotation objects retain source layer assignment, material, hidden state, component hierarchy, and flattened world transforms. Fields without a Blender counterpart, including arrow kind and dimension mode, remain available as custom properties. Blender does not provide SketchUp’s screen-space text or dimension system, so point-sized text is represented in model space and stored font family names are not resolved to operating-system font files automatically.
Material creation¶
BlenderSceneBuilder.build_material() converts one skppy material and is used
both by standalone SKM imports and by _build_materials(), which iterates
model.materials:
Reuses and updates materials already present in
bpy.data.materialsby name.Creates a new
bpy.data.materials.new(name)when needed.Enables
use_nodes = Trueand sets up a Principled BSDF node tree.Maps
color,alpha,metallic,roughnessto the BSDF inputs.For textured materials:
Writes texture bytes to a temporary file.
Loads the image with
bpy.data.images.load(temp_path).Packs the image into the
.blendwithimage.pack().Deletes the temp file.
Connects an
Image Texturenode to the BSDF Base Color and Alpha.Preserves the physical texture dimensions in the
skppy_x_scaleandskppy_y_scalecustom properties.
Sets
surface_render_method = "DITHERED"(Blender 4.2+) orblend_method = "HASHED"when transparency is needed, falling back toBLENDonly when the dithered option is unavailable.
The import operator first attempts model loading, then falls back to standalone
material loading only when the input is not a valid SKP model. This also
supports SKM downloads carrying a .skp filename. Standalone materials enable
Blender’s Fake User flag because they are intentionally created without an
object assignment.
Mesh and definition caching¶
_build_definitions() prepares skppy meshes for component definitions and
passes them to _build_mesh_from_prepared().
The cache key ensures that when the same definition is placed with different instance material overrides, separate Blender meshes are created (they would otherwise have different vertex colours / material assignments).
_build_mesh_from_prepared():
Calls
PreparedMesh.to_indexed()to get renderer-neutral indexed geometry from skppy.Creates a
bpy.data.meshes.new()and fills it withmesh.from_pydata().Assigns material slots from the resolved face material names.
Writes a Blender UV layer from skppy’s per-loop UV data when present.
Applies Blender-specific quad conversion when
triangulation_mode="QUADS".
TRIS uses skppy’s triangulation before Blender sees the mesh. NGONS keeps
skppy’s prepared polygons as-is where possible; single-hole SKP faces are split
into two simple n-gons because Blender mesh faces cannot store holes.
Cameras and scenes¶
_build_cameras() creates cameras from both model.cameras and cameras owned
only by saved Scene pages. A scene-owned camera uses the page name and keeps
the scene ID, description, hidden entity/layer IDs, active section planes, and
slideshow flag as custom properties. Scene cameras already present by identity
in model.cameras are not duplicated.
Perspective field of view and orthographic height are converted to Blender
camera settings. Eye, target, and up vectors form an orthonormal transform with
Blender’s local -Z viewing direction.
Instance hierarchy¶
_build_root_instances() processes model.entities.component_instances
and .groups.
Normal mode (flatten_hierarchy=False): A definition containing only its
own faces becomes a Mesh object directly at the instance transform. Definitions
that contain children, construction geometry, or annotations use this layout:
Empty (at instance transform)
+- Mesh object for direct faces (at identity transform)
\- Nested instance/helper/annotation objects
The Empty uses the instance’s
Transformas itsmatrix_local.Direct faces use an identity-transform child named
<instance>:faces.Nested contents inherit the transform through the parent-child relationship.
Mesh-only leaves omit the redundant Empty.
Compatible instances share the same
Meshdatablock.
Flat mode (flatten_hierarchy=True):
Accumulates
world_matrix = parent_world @ local_matrix.Creates a standalone mesh object with
matrix_world = world_matrix.No Empty parents.
Container-only nodes disappear, but their descendant geometry remains.
Compatible instances still share their cached
Meshdatablock.
Layer collection membership is independent of object parenting in either mode. See Imported Scene Organization for complete layouts and the mapping of SketchUp entity types to Blender objects.
Camera conversion¶
_build_cameras() creates one Blender camera per Camera in model.cameras:
Compute the right-hand basis:
F = normalize(target - eye)R = normalize(F x up)U = R x F
Build a Blender
Matrix(column-major,-Zforward,+Yup):[ Rx Ux -Fx eye.x * scale ] [ Ry Uy -Fy eye.y * scale ] [ Rz Uz -Fz eye.z * scale ] [ 0 0 0 1 ]
Set
camera_obj.matrix_world.Set
camera.data.lensfrom FOV.Set
camera.data.type = "PERSP"or"ORTHO".
Default material¶
Objects that have faces with no resolved material receive a slot filled with a material named “SKP Default” (base colour 0.8, 0.8, 0.8, roughness 1.0). This material is created once and reused.