A value that knows how to decode information from the OBJ file format
elm-obj-file supports triangular meshes that may have normal vectors and/or texture coordinates, polylines and points.
By default, the geometrical data is returned in the ObjCoordinates
coordinate system.
It's also possible to transform coordinates if desired.
Note that all primitive decoders require at least one element and will fail if no elements are found.
triangles : Decoder (TriangularMesh (Point3d Length.Meters ObjCoordinates))
Decode just the plain positions. Use with Scene3d.Mesh.indexedTriangles
and Scene3d.Mesh.indexedFacets
from elm-3d-scene.
faces : Decoder (TriangularMesh { position : Point3d Length.Meters ObjCoordinates, normal : Vector3d Quantity.Unitless ObjCoordinates })
Decode positions and normal vectors. Use with Scene3d.Mesh.indexedFaces
.
texturedTriangles : Decoder (TriangularMesh { position : Point3d Length.Meters ObjCoordinates, uv : ( Basics.Float, Basics.Float ) })
Decode positions and UV (texture) coordinates.
Use with Scene3d.Mesh.texturedTriangles
or Scene3d.Mesh.texturedFacets
.
texturedFaces : Decoder (TriangularMesh { position : Point3d Length.Meters ObjCoordinates, normal : Vector3d Quantity.Unitless ObjCoordinates, uv : ( Basics.Float, Basics.Float ) })
Decode positions, UV and normal vectors. Use with Scene3d.Mesh.texturedFaces
.
polylines : Decoder (List (Polyline3d Length.Meters ObjCoordinates))
points : Decoder (List (Point3d Length.Meters ObjCoordinates))
decodeString : (Basics.Float -> Length) -> Decoder a -> String -> Result String a
Run the decoder on the string. Takes a function, that knows how to convert float coordinates into physical units.
decodeString Length.meters triangles string == Ok (TriangularMesh {...})
decodeString Length.meters triangles string == Err "Line 1: Invalid OBJ syntax '...'"
expectObj : (Result Http.Error a -> msg) -> (Basics.Float -> Length) -> Decoder a -> Http.Expect msg
Load a mesh from an HTTP request.
type Msg
= GotMesh (Result Http.Error (TriangularMesh (Point3d Meters ObjCoordinates)))
getMesh : Cmd Msg
getMesh =
Http.get
{ url = "Pod.obj.txt"
, expect =
expectObj GotMesh
Length.meters
triangles
}
Note: the .txt extension is required to work with elm reactor
.
Primitives within OBJ files can be tagged with metadata such as object name, group names and materials.
Using the filtering decoders, you can selectively decode based on this metadata.
For advanced filtering rules check the filter
decoder.
object : String -> Decoder a -> Decoder a
Decode data for the given object name.
wheels : Decoder (TriangularMesh (Point3d Meters ObjCoordinates))
wheels =
object "wheels" triangles
group : String -> Decoder a -> Decoder a
Decode data for the given group name.
defaultGroup : Decoder a -> Decoder a
Decode data for the default group. This group has a special meaning, all elements are assigned to it if a group is not specified.
defaultGroup =
group "default"
material : String -> Decoder a -> Decoder a
Decode data for the given material name.
Decode useful information other than primitives. This can be useful to inspect the contents of the file.
Metadata decoders can be also composed with advanced decoders andThen
and
combine
to first get the metadata, and then filter the primitives.
objectNames : Decoder (List String)
Decode a sorted list of object names.
groupNames : Decoder (List String)
Decode a sorted list of group names.
materialNames : Decoder (List String)
Decode a sorted list of material names.
map : (a -> b) -> Decoder a -> Decoder b
Transform the decoder. For example, if you need to decode triangles’ vertices:
vertices : Decoder (List (Point3d Meters ObjCoordinates))
vertices =
map
(\triangularMesh ->
triangularMesh
|> TriangularMesh.vertices
|> Array.toList
)
triangles
map2 : (a -> b -> c) -> Decoder a -> Decoder b -> Decoder c
Join the result from two decoders. This lets you extract parts of the same OBJ file into separate meshes.
type alias Car =
{ wheels : TriangularMesh (Point3d Meters ObjCoordinates)
, base : TriangularMesh (Point3d Meters ObjCoordinates)
}
carDecoder : Decoder Car
carDecoder =
map2 Car
(object "wheels" triangles)
(object "base" triangles)
map3 : (a -> b -> c -> d) -> Decoder a -> Decoder b -> Decoder c -> Decoder d
map4 : (a -> b -> c -> d -> e) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e
map5 : (a -> b -> c -> d -> e -> f) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f
filter : ({ groups : List String, object : Maybe String, material : Maybe String } -> Basics.Bool) -> Decoder a -> Decoder a
Filter what should be decoded. For example, to implement the group
decoder from above:
group name =
filter
(\properties ->
List.member name properties.groups
)
oneOf : List (Decoder a) -> Decoder a
Try a bunch of different decoders. You will get the result from the first one that succeeds.
fail : String -> Decoder a
A decoder that always fails with a given error message. Use it in case you need custom error messages.
succeed : a -> Decoder a
A decoder that always succeeds with the result. May be useful in combination with oneOf
to
provide a placeholder mesh if decoding fails.
andThen : (a -> Decoder b) -> Decoder a -> Decoder b
Run one decoder and then run another decoder. Useful when you first want to look at metadata, and then filter based on that.
combine : List (Decoder a) -> Decoder (List a)
Combine multiple decoders together. For example, to extract meshes for all materials:
type alias MeshWithMaterial =
( String, TriangularMesh (Point3d Meters ObjCoordinates) )
trianglesForMaterials : String -> Decode (List MeshWithMaterial)
trianglesForMaterials names =
names
|> List.map
(\materialName ->
material materialName triangles
|> map (\mesh -> ( materialName, mesh ))
)
|> combine
-- Decode material names, and then decode
-- triangles for these materials
withMaterials : Decode (List MeshWithMaterial)
withMaterials =
materialNames |> andThen trianglesForMaterials
Coordinate system for decoded meshes.
trianglesIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (TriangularMesh (Point3d Length.Meters coordinates))
Transform coordinates when decoding. For example, if you need to render a mesh with Z-up, but it was exported with Y-up:
type ZUpCoords
= ZUpCoords
yUpToZUpFrame : Frame3d Meters ZUpCoords { defines : ObjCoordinates }
yUpToZUpFrame =
Frame3d.atOrigin
|> Frame3d.rotateAround
Axis3d.x
(Angle.degrees 90)
zUpTriangles : Decoder (TriangularMesh (Point3d Meters ZUpCoords))
zUpTriangles =
trianglesIn yUpToZUpFrame
facesIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (TriangularMesh { position : Point3d Length.Meters coordinates, normal : Vector3d Quantity.Unitless coordinates })
texturedTrianglesIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (TriangularMesh { position : Point3d Length.Meters coordinates, uv : ( Basics.Float, Basics.Float ) })
texturedFacesIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (TriangularMesh { position : Point3d Length.Meters coordinates, normal : Vector3d Quantity.Unitless coordinates, uv : ( Basics.Float, Basics.Float ) })
polylinesIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (List (Polyline3d Length.Meters coordinates))
pointsIn : Frame3d Length.Meters coordinates { defines : ObjCoordinates } -> Decoder (List (Point3d Length.Meters coordinates))