Materials and panda3d-gltf

I’m trying to export a simple scene of three cubes from blender to .gltf and import to panda3d using panda3d-gltf. It is working, but I am not getting any materials. Everything looks kinda of like I expect it to, except for “white” which appears a light shade of blue.

If I use .obj then everything looks/works as I’d expect, but that format doesn’t support some features that I want (Custom Properties).

cubes.mtl (611 Bytes) cubes.obj (3.1 KB) cubes.gltf (35.1 KB)

The .gltf contains the materials as i’d expect (on the 2 non-textured cubes). But nodePath.getMaterial() always returns None (script and output below). If pada3d-gltf converting the materials to a texture on-import? Is there a way to get it to import the materials?

Also, unrelated to panda3d-gltf: blender allows you to add “Custom Properties” to almost anything. For models/meshes I notice these come through the gltf import as tags (nodePath.getTag()) which is great, but the Material class doesn’t have that whole tagging system. It’d be cool if that whole tagging system (get/setTag, get/setPythonTag, etc) were a mixin/bass class so that more areas of panda3d could be taggable.

In the project I’m working on, I’d rather add “Custom Properties” to materials than to models since materials can be shared between models. Like if I’m modeling a car which is made up of Body, Engine, LeftSeat, RightSeat, and 4 (seperate) Wheel models, and I needed a Weight property (for example), I have to individually tag all of those pieces separately which is tedious, but if I could tag the material, then I would save myself a lot of work because the 2 seats and 4 wheels already share the same Material.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
import sys

def dumpMats(np, depth=0):
	print('%s%s: %s' % ('  ' * depth, np.name, np.getMaterial()))
	for child in np.children:
		dumpMats(child, depth + 1)

class App(ShowBase):
	def __init__(self):
		ShowBase.__init__(self)

		self.disableMouse()

		self.cubes = self.loader.loadModel('./cubes.gltf', noCache=True)
		self.cubes.reparentTo(self.render)

		self.camera.setPos(20,20,20)
		self.camera.lookAt(0,0,0)

		plight = PointLight('plight')
		plight.setColor((1,1,1,1))
		plnp = self.render.attachNewNode(plight)
		plnp.setPos(42,42,42)
		self.render.setLight(plnp)

		self.accept('escape', sys.exit)
		self.accept('q', sys.exit)
		self.accept('g', self.oobe)

		self.render.ls()
		dumpMats(self.cubes)


if __name__ == '__main__':
	app = App()
	app.run()

Output:

:display: loading display module: libpandagl.dll
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
:display:windisplay: OS version: 10.0.2.18362
:display:windisplay:   
:display:windisplay: max Mhz 3501000000, current Mhz 3501000000
:ShowBase: Default graphics pipe is wglGraphicsPipe (OpenGL).
:display: Created output of type wglGraphicsWindow
:ShowBase: Successfully opened window of type wglGraphicsWindow (OpenGL)
:audio(error):   load_dso(libnone.so) failed, will use NullAudioManager
:audio(error):     Path not found
:audio: NullAudioManager
:audio: NullAudioManager
:loader: loading file type module: p3assimp
:loader: loading file type module: p3ptloader
:loader: loading file type module: EntryPoint.parse('gltf = gltf.loader:GltfLoader')
:loader: Writing /c/Users/Tony/AppData/Local/Temp/tmplmcxqdgj.bam
:loader: Reading /c/Users/Tony/AppData/Local/Temp/tmplmcxqdgj.bam
PandaNode render S:(CullFaceAttrib LightAttrib RescaleNormalAttrib)
  ModelNode camera T:q(pos 20 20 20 hpr 135 -35.2644 2.09131e-06)
    Camera cam ( PerspectiveLens )
      PerspectiveLens fov = 48.1294 30
  ModelRoot Scene
    PandaNode WhiteCube
      GeomNode Cube (1 geoms: S:(MaterialAttrib TextureAttrib))
    PandaNode RedCube T:m(pos 4 0 0 scale 1.5 1 1)
      GeomNode Cube.001 (1 geoms: S:(MaterialAttrib TextureAttrib))
    PandaNode TexCube T:m(pos -3 0 0)
      GeomNode Cube.002 (1 geoms: S:(MaterialAttrib TextureAttrib))
  PointLight plight ( PerspectiveLens PerspectiveLens PerspectiveLens PerspectiveLens PerspectiveLens PerspectiveLens ):
    color 1 1 1 1
    attenuation 1 0 0
Scene: None
  WhiteCube: None
    Cube: None
  RedCube: None
    Cube.001: None
  TexCube: None
    Cube.002: None
:display: Closing wglGraphicsWindow
:ShowBase: User closed main window.
:ShowBase: Exiting ShowBase.
[Finished in 1191.3s]

Also, is the ls() output format described anywhere? I cant’ find it.

After loading from gltf, I am recursing through the nodes and trying to remove this TextureAttrib and replace it with my own programatically created material, but it is not doing anything (no error, just no effect).

mat = Material('mat')
mat.setBaseColor((1,0,0,1))

for np in model.findAllMatches('**/+GeomNode'):
	node = np.node()
	node.clearAttrib(MaterialAttrib)
	node.clearAttrib(TextureAttrib)
	np.setMaterial(mat)

model.ls()

Output

ModelRoot A Simple Car T:(pos 0 0 1.52588e-05 hpr 0 -90 0) S:(ColorScaleAttrib TransparencyAttrib)
  PandaNode Body T:m(pos 0 -83 0 scale 180 83 88)
    GeomNode Body (1 geoms: S:(MaterialAttrib TextureAttrib)) [] S:(MaterialAttrib)
  PandaNode Engine T:m(pos 64 -80 0)
    GeomNode Engine (1 geoms: S:(MaterialAttrib TextureAttrib)) [] S:(MaterialAttrib)
  PandaNode RSeat T:m(pos -56 -96 -40 scale 2.5 40 30)
    GeomNode RSeat (1 geoms: S:(MaterialAttrib TextureAttrib)) [] S:(MaterialAttrib)
  PandaNode LSeat T:m(pos -56 -96 40 scale 2.5 40 30)
    GeomNode Cube. (1 geoms: S:(MaterialAttrib TextureAttrib)) [] S:(MaterialAttrib)
  PandaNode GasTank T:m(pos -64 -36 0 scale 32 12 32)
    GeomNode GasTank (1 geoms: S:(MaterialAttrib TextureAttrib)) [] S:(MaterialAttrib)

Note the two S:() parts of the ls() output, that is the part I dont understand. That implies to me that the geoms have attribs, but afaik geoms do not have attribs. This problem doesn’t occur with models loaded by means other than panda3d-gltf.

Geoms can indeed have a RenderState directly associated with them.
To clear this state, you can do something like this:

geom_node = nodepath.node()

for i in range(geom_node.get_num_geoms()):
    geom_node.set_geom_state(i, RenderState.make_empty())

Ahh. Ok thanks. That worked.