LineSegs Color on render2d

Hi everyone,

I’m trying to add a global frame on the right corner of the window.
I succeeded in having the global orientation and the mouse/camera movement (with all the help of a previous topic :grin:) but the colors I placed on the line disappeared.
I tried to use a colored material with a white lamp but the arrows are just plain white :crazy_face:
Can someone tell what’s wrong?

Cheers all!

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

class Color(Material):
    """ Define a child Material class with the same parameters for every object
    except the color """
    def __init__(self, red, green, blue):
        """r, g, b : float
        Define a material that is colored"""
        Material.__init__(self)
        self.setDiffuse((red, green, blue, 1))
        self.setSpecular((.5, .5, .5, 1))
        self.setShininess(12.5)
        self.setAmbient((red, green, blue, 1))
        self.setEmission((0, 0, 0, 0))
        self.setTwoside(True)

class GlobalFrame(NodePath):
	def __init__(self, app):
		NodePath.__init__(self, 'Global frame')
		self.app = app
		self.arrows = []
		self.__gen_arrows()
		self.app.taskMgr.add(self.setOri, 'Set global orientation of global frame', appendTask=True)
	
	def __gen_arrows(self):
		"""Create global frame with each axis and parent it to render"""
		# Set position and relation
		self.reparentTo(self.app.a2dBottomRight)
		self.setPos((-0.1, 0, 0.1))
		
		# Draw axes using the vector coordinates as color
		axes = {'x':(1, 0, 0), 'y':(0, 1, 0), 'z':(0, 0, 1)}	
		for name, co_cl in axes.items(): 
			axe = LineSegs(name)
			axe.moveTo(0, 0, 0)
			axe.drawTo(*co_cl)
			axe.setThickness(5.0)
			axe = axe.create()
			axeNP = NodePath(axe)
			axeColor = Color(*co_cl)
			axeNP.setMaterial(axeColor)
			axeNP.reparentTo(self)
			axeNP.setColor(*co_cl)
			self.arrows.append(axeNP)
		self.setScale(0.1, 0.1, 0.1)
		self.app.viewChanged = True
		self.light = PointLight('Global Frame Light')
		self.light.setColor((1, 1, 1, 1))
		self.lightNP = NodePath(self.light)
		self.lightNP.reparentTo(self)
			
	def setOri(self, task):
		"""Set orientation of global frame"""
		if self.app.viewChanged:		
			self.setHpr(-self.app.camPivot.getHpr())
			self.app.viewChanged = False
		return task.again

class App(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.model=loader.load_model('teapot')
        self.model.reparent_to(render)
        point = PointLight('Point Light')
        point.set_color((1, 1, 1, 1))
        point = render.attachNewNode(point)
        point.reparentTo(self.model)
        self.render.setLight(point)
        point.setPos((10, 0, 0))
        point.lookAt((0, 0, 0))
        self.setup_view()
        self.globalFrame = GlobalFrame(self)

    def setup_view(self):
        """Disable the mouse and set up mouse-view functions"""
        self.disableMouse()    
        # Define camera parameters
        ## Camera angles
        self.camHorAng = 40
        self.camVerAng = 30
        self.camLens.setFov(self.camHorAng, self.camVerAng)
        ## Near/Far plane
        self.camNear = 1    
        self.camLens.setNear(self.camNear)
        self.camFar = 10000
        self.camLens.setFar(self.camFar)
        ## Camera pivot
        self.camPivot = self.render.attach_new_node("cam_pivot")
        self.cam.reparent_to(self.camPivot)
        self.cam.set_y(-100.)
        ## Camera step for changes
        self.camSpeed = .05
        self.camZoomStep = 5

        # Set up camera zoom
        self.accept('wheel_up', self.zoom_in)
        self.accept('wheel_down', self.zoom_out)
        
        # Set up camera rotation    
        self.accept('mouse2', self.wheel_down)
        self.accept('mouse2-up', self.wheel_up)
        self.lastMousePos = None
        self.wheel_pressed = False
        self.taskMgr.add(self.rotate_view, 'Rotate Camera View')

    # Functions for camera zoom
    def zoom_out(self):
        """Translate the camera along its local y axis to zoom out the view"""
        self.viewChanged = True
        self.cam.set_y(self.cam, -self.camZoomStep)

    def zoom_in(self):
        """Translate the camera along its local y axis to zoom in the view"""
        self.viewChanged = True
        self.cam.set_y(self.cam, self.camZoomStep)

    # Functions for camera rotation
    def wheel_down(self):
        self.wheel_pressed = True
        self.lastMousePos = None

    def wheel_up(self):
        self.wheel_pressed = False
        self.lastMousePos = None

    def rotate_view(self, task):
        if self.wheel_pressed and self.mouseWatcherNode.hasMouse():
            mouse_pos = self.mouseWatcherNode.getMouse()
            if self.lastMousePos is None:
                self.lastMousePos = Point2(mouse_pos)
            else:
                d_heading, d_pitch = (mouse_pos - self.lastMousePos) * 100.
                pivot = self.camPivot
                pivot.set_hpr(pivot.get_h() - d_heading, pivot.get_p() + d_pitch, 0.)
                self.viewChanged = True
                self.lastMousePos = Point2(mouse_pos)
        return task.again

app = App()
app.run()

EDIT:
I found a way :sweat_smile: by using an ambient light (with the color of the line) for each line us but it seems redundant.

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

class Color(Material):
    """ Define a child Material class with the same parameters for every object
    except the color """
    def __init__(self, red, green, blue):
        """r, g, b : float
        Define a material that is colored"""
        Material.__init__(self)
        self.setDiffuse((red, green, blue, 1))
        self.setSpecular((.5, .5, .5, 1))
        self.setShininess(12.5)
        self.setAmbient((red, green, blue, 1))
        self.setEmission((0, 0, 0, 0))
        self.setTwoside(True)

class GlobalFrame(NodePath):
	def __init__(self, app):
		NodePath.__init__(self, 'Global frame')
		self.app = app
		self.arrows = []
		self.__gen_arrows()
		self.app.taskMgr.add(self.setOri, 'Set global orientation of global frame', appendTask=True)
	
	def __gen_arrows(self):
		"""Create global frame with each axis and parent it to render"""
		# Set position and relation
		self.reparentTo(self.app.a2dBottomRight)
		self.setPos((-0.1, 0, 0.1))
		
		# Draw axes using the vector coordinates as color
		axes = {'x':(1, 0, 0), 'y':(0, 1, 0), 'z':(0, 0, 1)}	
		for name, co_cl in axes.items(): 
			axe = LineSegs(name)
			axe.moveTo(0, 0, 0)
			axe.drawTo(*co_cl)
			axe.setThickness(5.0)
			axe = axe.create()
			axeNP = NodePath(axe)
			axeColor = Color(*co_cl)
			axeNP.setMaterial(axeColor)
			axeNP.reparentTo(self)
			#axeNP.setColor(*co_cl)
			self.arrows.append(axeNP)
			light = AmbientLight('Global Frame Light')
			color = list(co_cl) + [1]
			light.setColor(tuple(color))
			lightNP = NodePath(light)
			lightNP.reparentTo(axeNP)
			axeNP.setLight(lightNP)
		self.setScale(0.1, 0.1, 0.1)
		self.app.viewChanged = True

			
	def setOri(self, task):
		"""Set orientation of global frame"""
		if self.app.viewChanged:		
			self.setHpr(-self.app.camPivot.getHpr())
			self.app.viewChanged = False
		return task.again

class App(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.model=loader.load_model('teapot')
        self.model.reparent_to(render)
        point = PointLight('Point Light')
        point.set_color((1, 1, 1, 1))
        point = render.attachNewNode(point)
        point.reparentTo(self.model)
        self.render.setLight(point)
        point.setPos((10, 0, 0))
        point.lookAt((0, 0, 0))
        self.setup_view()
        self.globalFrame = GlobalFrame(self)

    def setup_view(self):
        """Disable the mouse and set up mouse-view functions"""
        self.disableMouse()    
        # Define camera parameters
        ## Camera angles
        self.camHorAng = 40
        self.camVerAng = 30
        self.camLens.setFov(self.camHorAng, self.camVerAng)
        ## Near/Far plane
        self.camNear = 1    
        self.camLens.setNear(self.camNear)
        self.camFar = 10000
        self.camLens.setFar(self.camFar)
        ## Camera pivot
        self.camPivot = self.render.attach_new_node("cam_pivot")
        self.cam.reparent_to(self.camPivot)
        self.cam.set_y(-100.)
        ## Camera step for changes
        self.camSpeed = .05
        self.camZoomStep = 5

        # Set up camera zoom
        self.accept('wheel_up', self.zoom_in)
        self.accept('wheel_down', self.zoom_out)
        
        # Set up camera rotation    
        self.accept('mouse2', self.wheel_down)
        self.accept('mouse2-up', self.wheel_up)
        self.lastMousePos = None
        self.wheel_pressed = False
        self.taskMgr.add(self.rotate_view, 'Rotate Camera View')

    # Functions for camera zoom
    def zoom_out(self):
        """Translate the camera along its local y axis to zoom out the view"""
        self.viewChanged = True
        self.cam.set_y(self.cam, -self.camZoomStep)

    def zoom_in(self):
        """Translate the camera along its local y axis to zoom in the view"""
        self.viewChanged = True
        self.cam.set_y(self.cam, self.camZoomStep)

    # Functions for camera rotation
    def wheel_down(self):
        self.wheel_pressed = True
        self.lastMousePos = None

    def wheel_up(self):
        self.wheel_pressed = False
        self.lastMousePos = None

    def rotate_view(self, task):
        if self.wheel_pressed and self.mouseWatcherNode.hasMouse():
            mouse_pos = self.mouseWatcherNode.getMouse()
            if self.lastMousePos is None:
                self.lastMousePos = Point2(mouse_pos)
            else:
                d_heading, d_pitch = (mouse_pos - self.lastMousePos) * 100.
                pivot = self.camPivot
                pivot.set_hpr(pivot.get_h() - d_heading, pivot.get_p() + d_pitch, 0.)
                self.viewChanged = True
                self.lastMousePos = Point2(mouse_pos)
        return task.again

app = App()
app.run()

The materials don’t show up using a regular light source because geometry created using LineSegs doesn’t have normals; the axes will just be black (they were white previously because you hadn’t set any light to affect the GlobalFrame).

You can use vertex colours instead, which can be set using a call to axe.set_color(*co_cl), before any calls to axe.moveTo and axe.drawTo:

        for name, co_cl in axes.items(): 
            axe = LineSegs(name)
            axe.set_color(*co_cl)
            axe.moveTo(0, 0, 0)
            axe.drawTo(*co_cl)

Note that axeNP.setColor calls have no effect unless you use a priority value, as LineSegs sets a RenderState with a ColorAttrib at the Geom level.

Regarding the actual purpose of your GlobalFrame, it doesn’t work properly right now. To get the inverse of an orientation you can’t simply negate the corresponding hpr values; instead, you need the inverse of the quaternion representing that orientation. In your case, you can retrieve the quaternion of the world (“render”), relative to the camera pivot:

    def setOri(self, task):
        """Set orientation of global frame"""
        if self.app.viewChanged:        
            self.set_quat(self.app.render.get_quat(self.app.camPivot))
            self.app.viewChanged = False
        return task.again

Good luck with your project :slight_smile: .

Hi Epihaius,

Thx a lot for the answer (and the tip for the rotation) :grin:
I didn’t expect it to be so simple :rofl:

Cheers all!