Skip to content Skip to sidebar Skip to footer

How Do I Import An Stl Into Pygltflib

How do I import an STL file as a mesh into pygltflib? I have tried gltf = GLTF2().load(filename) but that only accepts a valid gltf.

Solution 1:

I used pygltflib and numpy-stl for this and it actually was not to hard, because .stl files typically meet these conditions:

  1. Only consist of triplets of points / vertecies that make up a triangle
  2. The order of the points does not matter, instead the face normals are stored
  3. The STL consists of only one object
  4. There are no colors or textures stored, just raw triangle meshes

With these ideas in mind I came up with a very basic implementation:

import pygltflib
import numpy as np
from stl import mesh
from math import sqrt

defnormalize(vector):
    norm = 0for i inrange(0, len(vector)):
        norm += vector[i] * vector [i]
    norm = sqrt(norm)
    for i inrange(0, len(vector)):
        vector[i] = vector[i] / norm

    return vector

stl_mesh = mesh.Mesh.from_file('cube.stl')

stl_points = []
for i inrange(0, len(stl_mesh.points)): # Convert points into correct numpy array
    stl_points.append([stl_mesh.points[i][0],stl_mesh.points[i][1],stl_mesh.points[i][2]])
    stl_points.append([stl_mesh.points[i][3],stl_mesh.points[i][4],stl_mesh.points[i][5]])
    stl_points.append([stl_mesh.points[i][6],stl_mesh.points[i][7],stl_mesh.points[i][8]])

points = np.array(
    stl_points,
    dtype="float32",
)

stl_normals = []
for i inrange(0, len(stl_mesh.normals)): # Convert points into correct numpy array
    normal_vector = [stl_mesh.normals[i][0],stl_mesh.normals[i][1],stl_mesh.normals[i][2]]
    normal_vector = normalize(normal_vector)
    stl_normals.append(normal_vector)
    stl_normals.append(normal_vector)
    stl_normals.append(normal_vector)

normals = np.array(
    stl_normals,
    dtype="float32"
)

points_binary_blob = points.tobytes()
normals_binary_blob = normals.tobytes()

gltf = pygltflib.GLTF2(
    scene=0,
    scenes=[pygltflib.Scene(nodes=[0])],
    nodes=[pygltflib.Node(mesh=0)],
    meshes=[
        pygltflib.Mesh(
            primitives=[
                pygltflib.Primitive(
                    attributes=pygltflib.Attributes(POSITION=0, NORMAL=1), indices=None
                )
            ]
        )
    ],
    accessors=[
        pygltflib.Accessor(
            bufferView=0,
            componentType=pygltflib.FLOAT,
            count=len(points),
            type=pygltflib.VEC3,
            max=points.max(axis=0).tolist(),
            min=points.min(axis=0).tolist(),
        ),
        pygltflib.Accessor(
            bufferView=1,
            componentType=pygltflib.FLOAT,
            count=len(normals),
            type=pygltflib.VEC3,
            max=None,
            min=None,
        ),
    ],
    bufferViews=[
        pygltflib.BufferView(
            buffer=0,
            byteOffset=0,
            byteLength=len(points_binary_blob),
            target=pygltflib.ARRAY_BUFFER,
        ),
        pygltflib.BufferView(
            buffer=0,
            byteOffset=len(points_binary_blob),
            byteLength=len(normals_binary_blob),
            target=pygltflib.ARRAY_BUFFER,
        ),
    ],
    buffers=[
        pygltflib.Buffer(
            byteLength=len(points_binary_blob) + len(normals_binary_blob)
        )
    ],
)
gltf.set_binary_blob(points_binary_blob + normals_binary_blob)
gltf.save("converted.glb")

I tested this with ASCII encoded .stl files generated in OpenSCAD, that can be found in this gist.

The first part, was converting the points and normals from numpy-stl's array layout, to the kind of array accepted by pygltflib. From there on it's just basic working with the pygltflib, to build the corresponding .gltf file.

Issues

The code has currently one small Issue.

  1. The models come out rotated

Fixing that should be easy, you would only have to apply a coordinate transformation to the points and normals. But because the model can be rotated in space freely nevertheless I did not bother to fix this.

Limitations

There are also some limitations, that could not easily be overcome. For example if you would like to assign different materials to different parts of the .stl this can not work, because there is quite literally no information about what a part is. Using only a single texture / color for the model is simple, though.

Post a Comment for "How Do I Import An Stl Into Pygltflib"