Names over heads of models

The title field is too short for it, but a more accurate title would be: Determining whether a Node or point in 3D space is being drawn.

I have a Node position roughly in the head of the player. Overall, I’d like to be able to know whether the camera can see the player’s head. My first attempt was to use inView(), but that doesn’t account for occlusion with other objects in the scene (i.e. if the player is behind a wall, inView is still true - which makes sense). Is there a quick way for me to know if this Node / Point is visible? It doesn’t need to be super accurate. I’d rather not use collision detection if possible.

The manual page here seems to suggest that a node might know if it’s hidden? panda3d.org/manual/index.php … oldid=7132

I also saw this thread, but I’m not sure if it’s applicable
panda3d.org/forums/viewtopic … 302b619947

It’s a difficult thing to do. Occlusion culling or collision detection will require significant CPU or GPU resources; there is no easy shortcut to get what you want.

If you are after name labels above characters head, then how about just using a camera facing billboard?

They will get smaller and bigger as the characters move to or away from the camera. But I think that could be fixed with a bit of counter-scaling… or maybe some orographic camera, color masks, offscreen buffers, render to texture magic?

Perhaps it would be better if you explained exactly what you’re trying to achieve, otherwise it’s difficult to come out with a solution.

What comes to my mind is ray casting, this is usually a good way to determine whether a point is occluded or not. Obviously, you can’t just shoot a ray from the camera to every single character in your scene, because that would be wasteful, but it can easily be optimized. For instance, you may only cast to those that are in front of the camera (or even within some cone, that’s not much math). Then you can limit the distance, and only cast to those not too far away. It’s also probable that you don’t need to check all characters in every frame.

That said, I’m not sure if the above solution is applicable.

I’m trying to replicate how games like Call of Duty, Battlefield 3, etc, do it. The player names are drawn on top of any of the world geometry, have a constant size, and only appear when the other player is visible. I recorded a quick video where you can see this. As I move behind the wall, the names of the now hidden players fade out and disappear. I can do the fading out part, I’m just not sure how to determine whether or not I can see the player. Here’s what I have:

Video: youtu.be/54zwmmKLqvY

Code:

class PlayerName():
    
    def __init__(self, name, parentNode):
        self.parentNode = parentNode
        
        self.nameTextNode = TextNode('nameTextNode')
        self.nameTextNode.setText(name)
        self.nameTextNode.setTextColor(Globals.COLOR_WHITE)
        self.nameTextNode.setShadow(0.05, 0.05)
        self.nameTextNode.setShadowColor(Globals.COLOR_BLACK)
        self.nameTextNode.set_align(TextNode.ACenter)
        self.nameTextNodeTextPath = aspect2d.attachNewNode(self.nameTextNode)
        self.nameTextNodeTextPath.setScale(Settings.CHAT_HEIGHT)
        
        self.hidden = True
        self.nameTextNodeTextPath.hide()
        
    def IsHidden(self):
        return self.hidden
    
    def FadeOut(self):
        self.hidden = True
        Sequence(LerpColorScaleInterval(self.nameTextNodeTextPath, 1, Globals.COLOR_TRANSPARENT),
                 Func(self.nameTextNodeTextPath.hide)).start()
    
    def Show(self):
        self.nameTextNodeTextPath.show()
        self.nameTextNodeTextPath.setColorScale(Globals.COLOR_WHITE)
        self.hidden = False
        
    def Update(self):
        self.nameTextNodeTextPath.setPos(Camera.Coord3dTo2d(self.parentNode))
             
        # If the node is visible  
        if(base.camNode.isInView(self.parentNode.getPos(base.cam))):
            if(self.IsHidden()):
                self.Show()
        else:
            if(not self.IsHidden()):
                self.FadeOut()

The parentNode is a node parented to the player set at a height above the model.

@Wezu That’s certainly something I thought of. However, you see in the video that for a brief moment the names are still visible on top of the occluding geometry as the name fades out - I don’t think that would be possible, because the name would get occluded by the geometry as well.

@CopperTop Yes, I can imagine determining which players are in front of the player, determining if their close enough, and then casting rays.

Hopefully what I’ve described will help you help me.

Extending my previous idea, I would think of drawing the names as GUI objects, instead of in 3D. That way you don’t need to worry about the world occluding the names or counter scaling and you can fade them however you like.

You can get the 2D point using the project(Point3, Point2) method on Lens. That should give you a 2D vector that you can use to place an OnscreenText.

@coppertop That’s what I’m doing. I should have probably indicated that in the code. The Update function gets called once each frame where the position of the TextNode is moved to the position of the parentNode after translating the 3D position of the parentNode to the 2D coords of aspect2d. That’s what this line does.

self.nameTextNodeTextPath.setPos(Camera.Coord3dTo2d(self.parentNode))

The nameTextNode is parented to aspect2d.

Here’s a video of how it currently looks, sorry for the poor quality. You can see that the Textnode is properly positioned, but using isInView() is still true regardless of whether the player is occluded (as expected). youtu.be/MZQvObuUQbU

Ah, I see. Sorry, I haven’t noticed you already use that approach. If your only problem is accounting for the objects being occluded, I don’t think you’ll find a better approach than ray casting from the camera to the other character’s head. I would suspect that’s how it’s done in BF3 and others.

Regarding collision detection. Would it be better to have a ray for each other player and traverse the scene once or to have one ray and traverse the scene for each play?