pywavefront / PyWavefront

Python library for importing Wavefront .obj files
BSD 3-Clause "New" or "Revised" License
312 stars 80 forks source link

Problems with wavefront object as GLMeshItem in Pyqtgraph #133

Closed Cocco17 closed 3 years ago

Cocco17 commented 3 years ago

Hi there! I'm trying to use PyWavefront to read an wavefront object and visualise it using Pyqtgraph. Using the faces and vertices data for GLLinePlotItem and GLScatterPlotItem works fine, but there seems to be a compatibility issue with the vertices and faces data with GLMeshItem:

> |==============================>>
>     |  Traceback (most recent call last):
>     |    File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 197, in _run_module_as_main
>     |      return _run_code(code, main_globals, None,
>     |    File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 87, in _run_code
>     |      exec(code, run_globals)
>     |    File "/Users/X/.vscode/extensions/ms-python.python-2021.3.680753044/pythonFiles/lib/python/debugpy/__main__.py", line 45, in <module>
>     |      cli.main()
>     |    File "/Users/X/.vscode/extensions/ms-python.python-2021.3.680753044/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 444, in main
>     |      run()
>     |    File "/Users/X/.vscode/extensions/ms-python.python-2021.3.680753044/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 285, in run_file
>     |      runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
>     |    File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 268, in run_path
>     |      return _run_module_code(code, init_globals, run_name,
>     |    File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 97, in _run_module_code
>     |      _run_code(code, mod_globals, init_globals,
>     |    File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 87, in _run_code
>     |      exec(code, run_globals)
>     |    File "/Users/X/test_pywavefront.py", line 45, in <module>
>     |      QtGui.QApplication.instance().exec_()
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/GLViewWidget.py", line 257, in paintGL
>     |      self.drawItemTree(useItemNames=useItemNames)
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/GLViewWidget.py", line 297, in drawItemTree
>     |      self.drawItemTree(i, useItemNames=useItemNames)
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/GLViewWidget.py", line 278, in drawItemTree
>     |      debug.printExc()
>     |    --- exception caught here ---
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/GLViewWidget.py", line 275, in drawItemTree
>     |      i.paint()
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/items/GLMeshItem.py", line 167, in paint
>     |      self.parseMeshData()
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/items/GLMeshItem.py", line 146, in parseMeshData
>     |      self.normals = md.vertexNormals(indexed='faces')
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/MeshData.py", line 209, in vertexNormals
>     |      faceNorms = self.faceNormals()
>     |    File "/Users/X/.venv/lib/python3.9/site-packages/pyqtgraph/opengl/MeshData.py", line 188, in faceNormals
>     |      self._faceNormals = np.cross(v[:,1]-v[:,0], v[:,2]-v[:,0])
>     |  TypeError: list indices must be integers or slices, not tuple
>     |==============================<<
> Error while drawing item <pyqtgraph.opengl.items.GLMeshItem.GLMeshItem object at 0x7f82a86dbdc0>.

Does anybody try to use PyWavefront in combination with Pyqtgraph already? Can any body help me to find the problem and a solution? Looking forward for some help, thanks a lot in advance!

The code:

  import pywavefront
  from pyqtgraph.Qt import QtCore, QtGui
  import pyqtgraph as pg
  import pyqtgraph.opengl as gl
  import numpy as np

  scene = pywavefront.Wavefront(
      "/BMW_simple.obj",
      strict=False,
      create_materials=True,
      collect_faces=True
  )

  print("Faces:", scene.mesh_list[0].faces)
  print("Vertices:", scene.vertices)
  print("Format:", scene.mesh_list[0].materials[0].vertex_format)
  print("Vertices:", scene.mesh_list[0].materials[0].vertices)

  app = QtGui.QApplication([])
  w = gl.GLViewWidget()
  w.show()
  w.setWindowTitle('pyqtgraph example: GLMeshItem')
  w.setCameraPosition(distance=40)

  g = gl.GLGridItem()
  g.scale(2,2,1)
  w.addItem(g)

  m1 = gl.GLMeshItem(vertexes=scene.vertices, drawFaces=False, drawEdges=True, smooth=True)
  m1.translate(5, 5, 0)
  m1.setGLOptions('additive')
  w.addItem(m1)

  pts2 = np.array(scene.vertices)
  sh2 = gl.GLLinePlotItem(pos=pts2, mode='line_strip')
  sh3 = gl.GLScatterPlotItem(pos=pts2)
  w.addItem(sh2)
  w.addItem(sh3)

  ## Start Qt event loop unless running in interactive mode.
  if __name__ == '__main__':
      import sys
      if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
          QtGui.QApplication.instance().exec_()
greenmoss commented 3 years ago

Hello, thanks for the bug report!

A few questions/requests:

Did you launch your command from a command line? If so, are you able to include the full command you used to launch, as well as reformatting the output to Markdown? You can use the same "```" technique as you used for the script contents.

Are you also able to include the contents of BMW_simple.obj?

Thanks!

Cocco17 commented 3 years ago

Hi @greenmoss! Thanks for the quick reply.

The commands are part a Jupyter notebook that I use to test parts of my code before applying them in my main code.

I just edited the output accordingly, sorry about that!

Attached you can find the Wavefront model - it's a model from the www that I simplified using Blender and exporting it to a wavefront object.

Another weird thing that is happening: In Blender I could choose the Forward and Top Axis, which I have chosen to be the same as in my GLViewWidget. However, when importing the model as a GLLinePlot, the model is rotate 90° to the left of the x-axis. Using the rotating function seems to fail as the model is then not shown anymore.. But I guess that is an issue with pyqtgraph, isn't it?

BMW_Easy.obj.zip

greenmoss commented 3 years ago

Thanks for the reformat!

Looking at your code, there are some print statements. Are those part of the output as well?

I don't have experience with Jupyter or Pyqtgraph. Based on the stack trace, I'd think File "/Users/X/test_pywavefront.py", line 45 is the place to look. Presumably that is line 45 of your code above?

Some things to try:

Cocco17 commented 3 years ago

Thanks for your input! Just managed to transform the outputs in the suitable format for PyQtGraph (extracted all faces into a separate array).

(Print statements are just for debugging purposes.. I have used one of the examples and exchanged the model and it works fine, so the problem is definitely due to format issues...)

nitieaj commented 3 years ago

Thanks for your input! Just managed to transform the outputs in the suitable format for PyQtGraph (extracted all faces into a separate array)

@Cocco17 Pls Can you point me in the right direction to towards how you managed to transform the output in the suitable format for pyqtgraph. ? I`m having simiar issues.

Cocco17 commented 3 years ago

Hi @nitieaj! Sorry for the late response, had a few days of vacation. This is the code I used to transform the pywavefront object to a plot in pyqtgraph:

        # Vehicle
        vehicle = scene = pywavefront.Wavefront('./vehicle.obj', strict=False, create_materials=True, collect_faces=True)#, cache=True) # Cache is currently not working?!

        # Conversion - Pywavefront to PyQtGraph GLMeshItem
        vertices_array = np.asarray(vehicle.vertices)
        faces_array = []
        for mesh_lists in vehicle.mesh_list:
            for faces in mesh_lists.faces:
                faces_array.append(np.array([faces[0],faces[1],faces[2]]))
        faces_array = np.asarray(faces_array)

        # Plotting the data in PyQtGraph
        vehicleMesh = gl.MeshData(vertexes=vertices_array, faces=faces_array)
        vehicleGL = gl.GLMeshItem(meshdata=vehicleMesh, drawEdges=True, edgeColor=(0,1,0,1), smooth=True)
        vehicleGL.scale(100,100,100)
        self.graphicsView.addItem(vehicleGL)
        self.graphicsView_viewResults.addItem(vehicleGL)

However, I did not manage to add the textures/materials of the model. I'm not sure whether this is possible or not with PyQtGraph - if you find a way, please let me know!

Furthermore, the cache function in pywavefront has not been working for me. The created binary file cannot be processed after its creation - again, if you find a solution, happy to hear back from you!