A flutter package that displays large 3D datasets on a transparent canvas

****** Important ******

  • Original project can be found ditredi.
  • Add layers and sort z-index by figures, to fix rendering problems when two figures are close enough to each other.
  • Rendering priority from high to low would be layers, figure’s z-index, and face’s z-index in each figure.

DiTreDi (D 3D)

A flutter package that displays large 3D datasets on a transparent canvas.

Live web example

Example source code

Preface

DiTreDi was created to efficiently display datasets and meshes in 3D space. It wasn’t intended to create a 3D game engine and is rather useful for displaying static meshes.

Table of Contents

Getting started

Install package.

Add imports for ditredi and vector_math_64:

import 'package:ditredi/ditredi.dart';
import 'package:vector_math/vector_math_64.dart';

Add DiTreDi widget to your tree:

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
    ],
)

And voilà, a single cube will be displayed:

Note: DiTreDi takes full available size. Wrap in SizedBox or Expanded to control its constraints and size.

Controller

DiTreDiController controls a scene rotation, scale, light.

To set up a controller, keep its reference in a state and pass to the controller parameter.

// in a state
@override
void initState() {
    super.initState();
    controller = DiTreDiController();
}

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
    ],
    controller: controller,
)

Once ready, update controller state by calling:

controller.update(rotationY: 30, rotationX: 30);

To handle input gestures use GestureDetector or DiTreDiDraggable:

DiTreDiDraggable(
    controller: controller,
    child: DiTreDi(
        figures: [Cube3D(1, vector.Vector3(0, 0, 0))],
        controller: controller,
    ),
);

Config

DiTreDiConfig defines component “defaults” – mesh color, lines and points width (if not specified).

// in a state

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
    ],
    config: DiTreDiConfig(
        supportZIndex: false,
    ),
)

If a huge dataset is displayed and you don’t bother about paint order, it’s recommended to disable z-index which boosts drawing speed (supportZIndex = false).

Shapes

DiTreDi out-of-the-box supports shapes like:

Cube3D

Just a cube.

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
    ],
)

Face3D

Face (aka triangle).

DiTreDi(
    figures: [
        Face3D(
            Triangle.points(
                Vector3(0, 0 - 1, 0 - 1),
                Vector3(0, 0 - 1, 0 + 1),
                Vector3(0, 0 + 1, 0 + 1),
            ),
        ),
    ],
)

Group3D

Groups a list of figures.

DiTreDi(
    figures: [
        Group3D([
            Cube3D(1, Vector3(0, 0, 0)),
            Cube3D(1, Vector3(3, 3, 3)),
        ]),
    ],
)

An alternative method to display multiple shapes, it to put a few figures:

DiTreDi(
    figures: [
        Cube3D(1, Vector3(0, 0, 0)),
        Cube3D(1, Vector3(-3, 0, 0)),
        Cube3D(1, Vector3(3, 0, 0)),
        Cube3D(1, Vector3(0, -3, 0)),
        Cube3D(1, Vector3(0, 3, 0)),
        Cube3D(1, Vector3(0, 0, -3)),
        Cube3D(1, Vector3(0, 0, 3)),
    ],
)

Line3D

A line.

DiTreDi(
    figures: [
      Line3D(Vector3(0, 1, 0), Vector3(2, 1, 4), width: 8),
      Line3D(Vector3(0, 2, 0), Vector3(2, 2, 4), width: 6),
      Line3D(Vector3(0, 3, 0), Vector3(2, 3, 4), width: 4),
      Line3D(Vector3(0, 4, 0), Vector3(2, 4, 4), width: 2),
      Line3D(Vector3(0, 5, 0), Vector3(2, 5, 4), width: 1),
    ],
)

Mesh3D

A mesh made of faces (triangles). You could use ObjParser to load it from .obj file contents. Currently, only material (mtl) as colours are supported.

Load from a string obj content:

DiTreDi(
    figures: [
        Mesh3D(await ObjParser().parse(meshLines)),
    ],
)

Load from a flutter resource:

DiTreDi(
    figures: [
        Mesh3D(await ObjParser().loadFromResources("assets/model.obj")),
    ],
)

Load from a file:

DiTreDi(
    figures: [
        Mesh3D(await ObjParser().loadFromFile(Uri.parse("files/model.obj"))),
    ],
)

Plane3D

A plane facing x (left/right), y (bottom/up) or z (near/far) axis.

DiTreDi(
    figures: [
        Plane3D(5, Axis3D.y, false, Vector3(0, 0, 0), color: Colors.green),
    ],
)

Point3D

A point (exactly a square).

DiTreDi(
    figures: [
        Plane3D(5, Axis3D.y, false, Vector3(0, 0, 0), color: Colors.green),
    ],
)

PointPlane3D

A plane made of points (e.g. to show an object) scale.

DiTreDi(
    figures: [
      PointPlane3D(1, Axis3D.y, 0.1, Vector3(0, 0, 0), pointWidth: 5),
      Cube3D(0.1, Vector3(0, 0, 0)),
    ],
)

Transformations

Points and wireframes

Each figure could be changed to points or lines (wireframe).

DiTreDi(
    figures: [
      ...Plane3D(5, Axis3D.z, false, Vector3(0, 0, 0)).toPoints(),
    ],
)

DiTreDi(
    figures: [
      ...Plane3D(5, Axis3D.z, false, Vector3(0, 0, 0)).toLines(),
    ],
)

Matrix transformation

Figures might be rotated, translated, and scaled with TransformModifier3D and Matrix4.

Each transformation is made for 0,0,0 coordinates.

DiTreDi(
    figures: [
        TransformModifier3D(
            Cube3D(2, Vector3(1, 1, 1)),
            Matrix4.identity()
              ..setRotationX(10)
              ..setRotationY(10)
              ..setRotationZ(10)),
    ],
)

To rotate the figure around its “own” position, you must translate, rotate and translate back.

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
        TransformModifier3D(
            Cube3D(2, Vector3(2, 2, 2)),
            Matrix4.identity()
              ..translate(2.0, 2.0, 2.0)
              ..rotateZ(10)
              ..rotateX(10)
              ..translate(-2.0, -2.0, -2.0)),
    ],
)

Transform Group3D to apply a matrix to each figure.

DiTreDi(
    figures: [
        Cube3D(2, Vector3(0, 0, 0)),
        TransformModifier3D(
          Group3D([
            Cube3D(2, Vector3(2, 2, 2)),
            Cube3D(2, Vector3(4, 4, 4)),
          ]),
          transformation,
        ),
    ],
)

Benchmarks

TBD

GitHub

View Github