How Do I Import An Stl Into Pygltflib
Solution 1:
I used pygltflib and numpy-stl for this and it actually was not to hard, because .stl files typically meet these conditions:
- Only consist of triplets of points / vertecies that make up a triangle
- The order of the points does not matter, instead the face normals are stored
- The STL consists of only one object
- 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.
- 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"