LCOV - code coverage report
Current view: top level - src/obj - writeobj.jl (source / functions) Hit Total Coverage
Test: on branch nothing Lines: 33 74 44.6 %
Date: 2025-07-10 13:29:30 Functions: 0 0 -

          Line data    Source code
       1             : """
       2             :     writeobjmesh(io, mesh[;
       3             :                  position=Val(:x), texcoord=Val(:u),
       4             :                  normal=nothing, color=Val(:c),
       5             :                  format="%g"])
       6             : 
       7             : Write `mesh` to `io` in [OBJ format](https://paulbourke.net/dataformats/obj/)
       8             : using attributes
       9             : 
      10             : - `position`
      11             : - `texcoord` optional vertex texture coordinates, possibly stored per face
      12             :    (as `hattr`).
      13             : - `normal` optional vertex normal, possibly stored per face
      14             :    (as `hattr`)
      15             : -  `color` optional vertex color
      16             :    ([format](https://paulbourke.net/dataformats/obj/colour.html))
      17             : 
      18             : Pass `nothing` to ignore an *optional* attribute. Attributes the are
      19             : disabled are also ignored, i.e., not written.
      20             : 
      21             : The optional `format` argument specifies the output format of a
      22             : floating point number as a `String` using `sprintf`-syntax.
      23             : 
      24             : !!! note
      25             :     This method is called by [`writemesh`](@ref).
      26             : 
      27             : See also [`writemesh`](@ref), [`readobjmesh`](@ref), [`readobj`](@ref)
      28             : """
      29           6 : function writeobjmesh(io::IO, mesh::Mesh{V, F, H, E};
      30             :                       position=Val(:x),
      31             :                       texcoord=Val(:u),
      32             :                       normal=nothing,
      33             :                       color=Val(:c),
      34             :                       format::String="%g") where {V, F, H, E}
      35           3 :     println(io, "# PMesh")
      36             : 
      37           3 :     formatter = generate_formatter(format)
      38             : 
      39             :     # NOTE: There may be unsued VHnd!
      40       11351 :     vindices = zeros(Int, PMesh.n_total(vattr(mesh)))
      41             : 
      42           3 :     ax = vattr(mesh, position)
      43             : 
      44           3 :     if PMesh.hasvattr(mesh, color) && isenabled(vattr(mesh, color))
      45           1 :         ac = vattr(mesh, color)
      46           2 :         for (i, v) in enumerate(vertices(mesh))
      47        9222 :             vindices[Int(v)] = i
      48        9222 :             print(io, "v ")
      49        9222 :             print(io, join(formatter.(ax[v]), ' '))
      50        9222 :             (length(eltype(ax)) == 2) && print(io, " 0")
      51        9222 :             print(io, "  ")
      52        9222 :             rbg = (ac[v].r, ac[v].g, ac[v].b)
      53       18444 :             println(io, join(formatter.(rbg), ' '))
      54       18443 :         end
      55             :     else
      56           4 :         for (i, v) in enumerate(vertices(mesh))
      57        2129 :             vindices[Int(v)] = i
      58        2129 :             print(io, "v ")
      59        2129 :             println(io, join(formatter.(ax[v]), ' '))
      60        2129 :         end
      61             :     end
      62             : 
      63             :     # TODO: We could search for equal uv coordinates and "reuse" indices
      64             :     #       Requires map. Probably not worthwhile as we read per half-edge.
      65             :     #       Same for normals.
      66             : 
      67           3 :     face_uv_idx = false
      68             : 
      69           3 :     if PMesh.hasvattr(mesh, texcoord) && isenabled(vattr(mesh, texcoord))
      70           0 :         au = vattr(mesh, texcoord)
      71             : 
      72           0 :         for v in vertices(mesh)
      73           0 :             print(io, "vt ")
      74           0 :             println(io, join(formatter.(au[v]), ' '))
      75           0 :         end
      76           3 :     elseif PMesh.hashattr(mesh, texcoord) && isenabled(vattr(mesh, texcoord))
      77           0 :         au = hattr(mesh, texcoord)
      78             : 
      79           0 :         for f in faces(mesh)
      80           0 :             for h in halfedges(mesh, f) # Reuse indexing
      81           0 :                 print(io, "vt ")
      82           0 :                 print(io, join(formatter.(au[h]), ' '))
      83           0 :                 println(io)
      84           0 :             end
      85           0 :         end
      86             : 
      87           0 :         face_uv_idx = true
      88             :     end
      89             : 
      90           3 :     face_normal_idx = false
      91             : 
      92           3 :     if PMesh.hasvattr(mesh, normal) && isenabled(vattr(mesh, normal))
      93           0 :         an = vattr(mesh, normal)
      94             : 
      95           0 :         for v in vertices(mesh)
      96           0 :             print(io, "vn ")
      97           0 :             println(io, join(formatter.(an[v]), ' '))
      98           0 :         end
      99           3 :     elseif PMesh.hashattr(mesh, normal) && isenabled(vattr(mesh, normal))
     100           0 :         au = hattr(mesh, normal)
     101             : 
     102           0 :         for f in faces(mesh)
     103           0 :             for h in halfedges(mesh, f) # Reuse indexing
     104           0 :                 print(io, "vn ")
     105           0 :                 print(io, join(formatter.(an[h]), ' '))
     106           0 :             end
     107           0 :             println(io)
     108           0 :         end
     109             : 
     110           0 :         face_normal_idx = true
     111             :     end
     112             : 
     113           6 :     if face_uv_idx || face_normal_idx
     114           0 :         nh = 0
     115           0 :         for f in faces(mesh)
     116           0 :             print(io, "f")
     117           0 :             vidx = map(v -> vindices(v), Int.(vertices(mesh, f)))
     118           0 :             hidx = nh+1:length(vidx)
     119             : 
     120           0 :             for (v, h) in zip(vidx, hidx)
     121           0 :                 face_uv_idx && face_normal_idx && print(io, " $(v)/$(h)/$(h)")
     122           0 :                 face_uv_idx && print(io, " $(v)/$(h)")
     123           0 :                 face_normal_idx && print(io, " $(v)//$(h)")
     124           0 :             end
     125             : 
     126           0 :             println(io)
     127           0 :             hidx += length(vidx)
     128           0 :         end
     129             :     else
     130           6 :         for f in faces(mesh)
     131       21714 :             print(io, "f ")
     132       21714 :             println(io, join(Int.(vertices(mesh, f)), ' '))
     133       21714 :         end
     134             :     end
     135             : 
     136           3 :     nothing
     137             : end

Generated by: LCOV version 1.16