openskp 1.1.0
openskp: ^1.1.0 copied to clipboard
A pure Dart OpenSKP parser and writer - extract, create, and edit SketchUp (.skp) geometry, materials, and layers; export to GLB, OBJ, STL, PLY, DXF, and IFC4.
OpenSKP #
The open-source SketchUp (.skp) file parser โ Dart / Flutter edition.
Parse .skp files without SketchUp. No SDK. No license. Just code.
๐ openskp.com ยท ๐ Try the Live Web Viewer ยท ๐ Docs ยท Changelog
Important
This project was built by reverse engineering a proprietary binary format. It is not affiliated with or endorsed by Trimble Inc. or SketchUp.
๐ What is OpenSKP? #
OpenSKP is the first and only open-source, cross-platform parser for
SketchUp binary files โ reverse-engineered from both the modern VFF
container (SketchUp 2021+) and the classic MFC CArchive container
(SketchUp 2013โ2020). It gives Dart and Flutter developers full
programmatic access to geometry, materials, components, layers, and
metadata, with no SketchUp installation and no proprietary SDK required.
The same parser and export API also ship as first-class packages for
Python, TypeScript, .NET, and C++ โ see the
project README for the full
cross-language picture.
This package can also write new .skp files from scratch, and edit
existing ones, validated feature-by-feature against the real SketchUp
SDK (see Writing below).
๐ Vision & Platform Coverage #
Enable mobile (iOS/Android), desktop, and web developers to parse and build 3D SketchUp file viewer pipelines natively inside Flutter.
- Mobile: Flutter iOS & Android apps
- Desktop: Flutter Windows, macOS, and Linux
- Web: Compile client-side for browser-based parsers
- Server: Dart shelf backend parsing services
๐ Try the Live Web Viewer (Drag-and-Drop) #
โจ Features #
- Zero Native Dependencies: 100% pure Dart implementation.
- Full-fidelity parsing: vertices, edges, faces, normals, UV coordinates, nested component hierarchies, layers/tags, materials, textures, styles, and dynamic-component attributes.
- Both SketchUp file generations: modern VFF (2021+) and legacy MFC
(2013โ2020) containers, transparently, behind one
parse()call. - Scene baking: an opt-in
buildScene()pass resolves the full placed scene graph to world-space, triangulated, export-ready geometry. - Native multi-format export: glTF (GLB), Wavefront OBJ/MTL, STL, PLY, AutoCAD DXF (3DFACE and Polyface Mesh), IFC4 (BIM/ISO 10303-21 STEP) โ all written from scratch, no third-party CAD/BIM SDK involved. The DXF writer is verified against real desktop AutoCAD, not just lenient DXF readers.
- Write support: build new legacy-format
.skpfiles from scratch: geometry (including true, editable circular/arc curves, freeform polylines, faces with holes cut out, and non-planar auto-triangulation), materials (solid + PNG/JPEG textures), layers, nested component definitions and groups, instance rotation/visibility, and custom attribute dictionaries โ or load and extend an existing file withopenExisting(). No SDK involved; every feature validated against the real SketchUp SDK. See Writing below.
๐ Installation #
Add the library to your Dart or Flutter project:
# For Dart projects
dart pub add openskp
# For Flutter projects
flutter pub add openskp
๐ป Quick Start #
1. Parsing a SketchUp File #
Open a .skp file, read the byte buffer, and load the data model:
import 'dart:io';
import 'package:openskp/openskp.dart';
void main() async {
// Read SKP file bytes
final file = File('my_model.skp');
final bytes = await file.readAsBytes();
// Load and parse SKP model
final skpFile = SkpFile.fromBuffer(bytes);
final model = skpFile.parse();
print('SketchUp File Version: ${model.version}');
// Inspect Layers
print('Layers:');
for (var layer in model.layers) {
print('- ${layer.name} (RGB: ${layer.colorR}, ${layer.colorG}, ${layer.colorB})');
}
// Inspect Materials
print('Materials:');
for (var material in model.materials) {
print('- ${material.name} (Opacity: ${material.transparency})');
}
// Walk component definitions and their geometry
model.definitions.forEach((id, def) {
print('Definition $id: ${def.name} - ${def.vertices.length} vertices, ${def.faces.length} faces');
});
// model.root holds whatever is placed directly in the model (not inside
// any component/group), including root-level instances.
print('Root-level instances: ${model.root.instances.length}');
}
2. Baking Scene Graph & GLB Export #
Bake all placed instances into world-space, triangulated mesh primitives ready for 3D rendering or GLB export:
import 'dart:io';
import 'package:openskp/openskp.dart';
void main() async {
final bytes = await File('my_model.skp').readAsBytes();
final skpFile = SkpFile.fromBuffer(bytes);
// Bake scene graph into world-space meshes
final scene = skpFile.buildScene();
print('Renderable primitives: ${scene.glbPrimitives.length}');
for (var entry in scene.meshIndex.entries) {
print('- Mesh ${entry.key}: ${entry.value.definitionName} [${entry.value.layer}]');
}
// Export to binary glTF 2.0 (GLB) bytes or file
final glbBytes = toGlb(scene);
await exportGlb(scene, 'my_model.glb');
print('Exported GLB: ${glbBytes.length} bytes');
// Export to Wavefront OBJ, plus a companion .mtl material library
final objText = toObj(scene);
final mtlText = toMtl(scene);
exportObj(scene, 'my_model.obj'); // writes .obj + .mtl together
// Export to STL (3D printing), ASCII or little-endian binary
final stlBytes = toStlBinary(scene);
exportStl(scene, 'my_model.stl', binary: true);
// Export to PLY (Stanford Triangle Format), ASCII or little-endian binary
final plyBytes = toPlyBinary(scene);
exportPly(scene, 'my_model.ply', binary: true);
// Export to 3D DXF (AutoCAD R2000 compliant, Polyface Mesh by default)
final dxfText = toDxf(scene);
exportDxf(scene, 'my_model.dxf');
// Export to IFC4 / BIM (ISO 10303-21 STEP format)
final ifcText = toIfc(scene);
exportIfc(scene, 'my_model.ifc');
}
โ๏ธ Writing #
OpenSKP can also create new .skp files from scratch โ a genuine,
from-scratch binary writer for the legacy MFC CArchive format (SketchUp
2013โ2020), with no SketchUp SDK involved at any point. Ports the same
feature set as the Python package's writer, verified byte-identical to
Python's own output on the same input: geometry, materials (solid +
PNG/JPEG textures), layers (with color and default visibility),
component definitions with multiple instances, groups, nested
definitions and nested group instances, per-instance rotation and
visibility, explicit per-side texture positioning, custom key/value
attribute dictionaries, circular faces and partial arcs, freeform
polyline curves, faces with holes cut out, and non-planar
auto-triangulation. openExisting() loads an existing legacy-format
file and rebuilds it as a new builder, so more geometry can be added
before saving. See lib/src/create.dart for the
full scope notes.
import 'package:openskp/openskp.dart';
void main() {
final builder = create();
// Materials and layers
final red = builder.addMaterial('Red', [255, 0, 0]);
final roof = builder.addLayer('Roof', color: [180, 60, 40]);
// All addComponentDefinition/addGroup calls must come before any
// addInstance/addFace call - placing anything locks in the file's
// internal slot numbering for everything after it
final chair = builder.addComponentDefinition('Chair', (def) {
def.addFace([(0.0, 0.0, 0.0), (20.0, 0.0, 0.0), (20.0, 20.0, 0.0), (0.0, 20.0, 0.0)]);
});
builder.addInstance(chair, translation: (50.0, 0.0, 0.0));
builder.addInstance(chair, translation: (100.0, 0.0, 0.0), hidden: true);
builder.addFace(
[(0.0, 0.0, 0.0), (100.0, 0.0, 0.0), (100.0, 100.0, 0.0), (0.0, 100.0, 0.0)],
material: red, layer: roof,
);
builder.save('output.skp');
}
Editing an existing file #
final result = openExisting('building.skp');
for (final w in result.warnings) print('not fully reproduced: $w');
result.builder.addCircle((0.0, 0.0, 100.0), (0.0, 0.0, 1.0), 50.0);
result.builder.save('building_edited.skp');
result.warnings is the honest account of what couldn't be faithfully
reproduced from that specific source file. Every material/layer the
source had is reachable on result.builder.materialsByName/
layersByName without a separate lookup, and result.definitions maps
each replayed component definition's own name to its builder for placing
more instances of something the source already defined.
๐ API Data Model Reference #
The public API is designed to mirror the Python reference implementation's data model (the same shape the C# port also follows):
SkpModel #
String versionโ The parsed SketchUp application version.String? unitsโ Model unit-system string (e.g.,"Millimeter").Map<int, Definition> definitionsโ Component/group geometry definitions, keyed by their numeric TLV entity ID.Definition rootโ Whatever is placed directly in the model (not inside any component/group).List<Layer> layersโ Layer names, colors, andhiddenvisibility flags.List<Material> materialsโ Material names, colors, transparency, and embedded textures.Map<int, Material> materialsByIdโ Join table from a TLV material ID (Face.materialId) to itsMaterial.List<Style> stylesโ Bundled rendering styles (default front/back face colors).
Scene & GLB Export #
Scene buildScene()โ Opt-in scene graph flattener; resolves nested instance transforms into world-space meshes.List<GlbPrimitive> glbPrimitivesโ Triangulated mesh primitives ready for GPU upload or GLB packaging.Map<String, MeshMetadata> meshIndexโ Metadata map describing each baked mesh primitive, keyed by the matchingGlbPrimitive.geomName.Uint8List toGlb(Scene scene)โ Serializes a baked scene into binary glTF 2.0 (GLB) bytes.Future<File> exportGlb(Scene scene, String path)โ Exports a baked scene directly to a.glbfile on disk.
๐ญ Used in Production #
OpenSKP powers the SketchUp import pipeline for FrameSmart (a 3D collaboration platform with nearly 200 active users) and IngeTrazo (a SketchUp-alternative 3D modeler with a BIM โ IFC bridge). Using OpenSKP in your own project? Open an issue or a PR to get added here.
๐ License #
This library is open-source software licensed under the MIT License โ see the LICENSE file for details.