assimp 0.0.5

  • Readme
  • Changelog
  • Example
  • Installing
  • new43

Assimp for Dart #

pub license: BSD build codecov

This library provides Dart FFI bindings to Open Asset Import Library aka. Assimp, which is a portable library to import and export various well-known 3D model formats in a uniform manner.

Usage #

import 'package:assimp/assimp.dart';

final scene = Scene.fromFile('path/to/3d-model.obj');
for (final mesh in scene.meshes) {
  // ...
}

A little example of what you can do with Assimp:

Assimp

To use this package, add assimp as a dependency in your pubspec.yaml file.

Status #

This package is currently in alpha stage. It supports 64-bit Assimp 5.x, and has been tried out on the following platforms:

  • macOS (brew install assimp)
  • Linux (apt install libassimp5)
  • Windows (vcpkg install assimp:x64-windows)
  • Android
  • iOS

The documentation is still more or less direct copy-paste from the original library, and is therefore full of broken references.

Notice also that dart:ffi is still in beta, and many fundamentally important features are still in research and development:

[0.0.5] - 2020-07-02 #

  • Added support for import properties
  • Improved the example app

[0.0.4] - 2020-06-30 #

  • Overhauled the example app
  • Sorted out Dart Analyzer issues

[0.0.3] - 2020-06-28 #

  • Added Face.indexData
  • Added Mesh.vertexData, Mesh.normalData, Mesh.tangentData, Mesh.bitangentData
  • Allowed specifying ASSIMP_LIBRARY_PATH and/or ASSIMP_LIBRARY_PATH environment variables
  • Added a preliminary example

[0.0.2] - 2020-06-23 #

  • Added ImportFormat & Assimp.importFormats
  • Added ExportFormat & Assimp.exportFormats
  • Added Assimp.extensions & Assimp.isSupported(extension)
  • Added Scene.copy()
  • Added Material.textures & TextureType
  • Added Scene.exportFile()
  • Added ExportData & Scene.exportData()
  • Renamed PostProcess to ProcessFlags
  • Removed unused Ray & Plane (use vector_math)

[0.0.1] - 2020-06-21 #

  • Initial alpha release that supports read-only imports with Assimp 5.x on 64-bit desktop platforms.

example/lib/main.dart

import 'dart:collection';
import 'dart:math' as Math;
import 'dart:typed_data';
import 'dart:ui';

import 'package:assimp/assimp.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:vector_math/vector_math.dart' hide Colors;

void main() => runApp(ExampleApp());

const Map<String, Offset> models = {
  'box.obj': Offset(50, 50),
  'ear.stl': Offset(-325, -325),
  'spider.stl': Offset(-325, -325),
};

class ExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ExamplePage(),
    );
  }
}

class ExamplePage extends StatefulWidget {
  ExamplePage({Key key}) : super(key: key);

  @override
  _ExamplePageState createState() => _ExamplePageState();
}

class _ExamplePageState extends State<ExamplePage> {
  Scene scene;
  Aabb3 bounds;
  String current;
  Offset startPoint;
  double scale = 1;
  double startScale = 1;

  @override
  void initState() {
    super.initState();
    loadScene(models.keys.first);
  }

  Future<void> loadScene(String key) async {
    final data = await rootBundle.load('models/$key');
    final bytes =
        data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
    scene = Scene.fromBytes(bytes,
        hint: extension(key),
        flags: ProcessFlags.triangulate |
            ProcessFlags.optimizeMeshes |
            ProcessFlags.generateNormals |
            ProcessFlags.joinIdenticalVertices);
    current = key;
    bounds = scene.calculateBounds();
    transformScene(scale: 1, delta: models[key]);
  }

  void transformScene({double scale, Offset delta}) {
    if (scene == null) return;
    scene.rootNode.transformation = scene.rootNode.transformation
      ..rotateX(-0.01 * delta.dy)
      ..rotateY(0.01 * delta.dx);
    scene.postProcess(ProcessFlags.preTransformVertices);
    setState(() => this.scale = scale);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Assimp for Dart'),
        actions: [
          PopupMenuButton<String>(
            itemBuilder: (context) => models.keys
                .map(
                  (model) => CheckedPopupMenuItem<String>(
                    value: model,
                    checked: model == current,
                    child: Text(model),
                  ),
                )
                .toList(),
            onSelected: (value) => loadScene(value),
          ),
        ],
      ),
      body: GestureDetector(
        child: CustomPaint(
          painter: ScenePainter(scene, bounds, scale),
          size: MediaQuery.of(context).size,
        ),
        onScaleStart: (details) {
          startScale = scale;
          startPoint = details.focalPoint;
        },
        onScaleUpdate: (details) {
          final scaled = details.scale != 1.0 || details.rotation != 0.0;
          transformScene(
            delta: details.focalPoint - startPoint,
            scale: scaled ? startScale * details.scale : scale,
          );
          startPoint = details.focalPoint;
        },
      ),
    );
  }
}

extension Vector3List on Float32List {
  Vector3 vectorAt(int i) => Vector3(this[i], this[i + 1], this[i + 2]);
}

extension Vector3Bounds on Vector3 {
  Vector3 minV(Vector3 other) {
    return Vector3(
      Math.min(x, other.x),
      Math.min(y, other.y),
      Math.min(z, other.z),
    );
  }

  Vector3 maxV(Vector3 other) {
    return Vector3(
      Math.max(x, other.x),
      Math.max(y, other.y),
      Math.max(z, other.z),
    );
  }
}

extension SceneBounds on Scene {
  Aabb3 calculateBounds() {
    Aabb3 bounds;
    for (final mesh in meshes) {
      for (final vertex in mesh.vertices) {
        bounds = Aabb3.minMax(
          bounds?.min?.minV(vertex) ?? vertex,
          bounds?.max?.maxV(vertex) ?? vertex,
        );
      }
    }
    return bounds;
  }
}

class ScenePainter extends CustomPainter {
  final Scene scene;
  final Aabb3 bounds;
  final double scale;

  ScenePainter(this.scene, this.bounds, this.scale);

  @override
  void paint(Canvas canvas, Size size) {
    if (scene == null) return;

    final color = Colors.white;
    final light = Vector3(0.0, 0.0, bounds.max.z / scale);
    final extent = bounds.max.distanceTo(bounds.min);
    final transformation = scene.rootNode.transformation;

    final matrix = transformation
      ..translate(size.width / 2, size.height / 2, 0)
      ..scale(size.shortestSide / extent * scale);

    for (final mesh in scene.meshes) {
      final normals = mesh.normalData;
      final vertices = mesh.vertexData;

      double faceDepth(Uint32List indices, Float32List vertices) {
        return (vertices[indices[0] * 3 + 2] +
                vertices[indices[1] * 3 + 2] +
                vertices[indices[2] * 3 + 2]) /
            3;
      }

      final faces = SplayTreeMap<double, Face>.fromIterable(mesh.faces,
          key: (f) => faceDepth(f.indexData, vertices), value: (f) => f);

      final count = faces.length * 3;
      final colors = Int32List(count);
      final indices = Uint16List(count);
      final positions = Float32List(count * 2);

      var i = 0;
      for (final face in faces.values) {
        for (final j in face.indices) {
          final c = light.dot(matrix.transformed3(normals.vectorAt(j * 3)));
          if (c < 0 || c.isNaN) continue;

          colors[i] = Color.fromARGB(
            255,
            (color.red / 255 * c).round(),
            (color.green / 255 * c).round(),
            (color.blue / 255 * c).round(),
          ).value;

          final v = matrix.transformed3(vertices.vectorAt(j * 3));
          positions[i * 2] = v.x;
          positions[i * 2 + 1] = v.y;

          indices[i] = i++;
        }
      }

      if (i != 0) {
        final raw = Vertices.raw(
          VertexMode.triangles,
          positions.sublist(0, i * 2),
          colors: colors.sublist(0, i),
          indices: indices,
        );
        canvas.drawVertices(raw, BlendMode.src, Paint());
      }
    }
  }

  @override
  bool shouldRepaint(ScenePainter old) => true;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  assimp: ^0.0.5

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:assimp/assimp.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
5
Health:
Code health derived from static analysis. [more]
75
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
43
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14

Analysis suggestions

Package not compatible with runtime flutter-web on web

Because:

  • package:assimp/assimp.dart that imports:
  • package:assimp/src/texture.dart that imports:
  • package:assimp/src/type.dart that imports:
  • package:assimp/src/extensions.dart that imports:
  • package:assimp/src/bindings.dart that imports:
  • package:assimp/src/bindings/ai_vertex_weight.dart that imports:
  • dart:ffi

Package not compatible with runtime js

Because:

  • package:assimp/assimp.dart that imports:
  • package:assimp/src/texture.dart that imports:
  • package:assimp/src/type.dart that imports:
  • package:assimp/src/extensions.dart that imports:
  • package:assimp/src/bindings.dart that imports:
  • package:assimp/src/bindings/ai_vertex_weight.dart that imports:
  • dart:ffi

Health suggestions

Fix lib/src/bindings/ai_anim_mesh.dart. (-25 points)

Analysis of lib/src/bindings/ai_anim_mesh.dart reported 1042 hints, including:

line 55 col 7: The value of the field '_slen' isn't used.

line 57 col 7: The value of the field '_s0' isn't used.

line 57 col 12: The value of the field '_s1' isn't used.

line 57 col 17: The value of the field '_s2' isn't used.

line 57 col 22: The value of the field '_s3' isn't used.

Fix lib/src/bindings/ai_animation.dart. (-25 points)

Analysis of lib/src/bindings/ai_animation.dart reported 1029 hints, including:

line 57 col 7: The value of the field '_slen' isn't used.

line 59 col 7: The value of the field '_s0' isn't used.

line 59 col 12: The value of the field '_s1' isn't used.

line 59 col 17: The value of the field '_s2' isn't used.

line 59 col 22: The value of the field '_s3' isn't used.

Fix lib/src/bindings/ai_bone.dart. (-25 points)

Analysis of lib/src/bindings/ai_bone.dart reported 1041 hints, including:

line 55 col 7: The value of the field '_slen' isn't used.

line 57 col 7: The value of the field '_s0' isn't used.

line 57 col 12: The value of the field '_s1' isn't used.

line 57 col 17: The value of the field '_s2' isn't used.

line 57 col 22: The value of the field '_s3' isn't used.

Fix additional 20 files with analysis or formatting issues. (-315.77 points)

Additional issues in the following files:

  • lib/src/bindings/ai_camera.dart (1034 hints)
  • lib/src/bindings/ai_export_data_blob.dart (1026 hints)
  • lib/src/bindings/ai_light.dart (1045 hints)
  • lib/src/bindings/ai_material_property.dart (1026 hints)
  • lib/src/bindings/ai_mesh.dart (1055 hints)
  • lib/src/bindings/ai_mesh_anim.dart (1025 hints)
  • lib/src/bindings/ai_mesh_morph_anim.dart (1025 hints)
  • lib/src/bindings/ai_node.dart (1044 hints)
  • lib/src/bindings/ai_node_anim.dart (1027 hints)
  • lib/src/bindings/ai_string.dart (1024 hints)
  • lib/src/bindings/ai_texture.dart (1028 hints)
  • lib/src/properties.dart (86 hints)
  • lib/src/bindings/ai_key.dart (10 hints)
  • lib/src/bindings/ai_scene.dart (8 hints)
  • lib/src/bindings/ai_aabb.dart (6 hints)
  • lib/src/bindings/ai_uv_transform.dart (4 hints)
  • lib/src/bindings/ai_metadata.dart (2 hints)
  • lib/src/bindings/ai_face.dart (1 hint)
  • lib/src/bindings/ai_import_format_desc.dart (1 hint)
  • lib/src/scene.dart (Run dartfmt to format lib/src/scene.dart.)

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
ffi ^0.1.3 0.1.3
meta ^1.1.8 1.2.2 1.3.0-nullsafety
quiver ^2.1.3 2.1.3
vector_math ^2.0.8 2.0.8 2.1.0-nullsafety
Transitive dependencies
matcher 0.12.8
stack_trace 1.9.5
Dev dependencies
path ^1.7.0 1.7.0
test ^1.14.7
test_coverage ^0.4.1