Icons for 3D objects

What I’m trying to do is almost identical to the bomb and flag indicators in Call of Duty. I’d like to have an icon for certain objects that is drawn in aspect 2d and is place on top of the 3d object when the object is in view, and is pinned to the sides of the screen when it is not in view.

[1] is how it currently looks
[2] is an example of the ‘pinned to the side’ thing I mentioned
[3] I’d like to be able to know when the icon is in the center of the screen so I can make it more transparent

What I have so far doesn’t really cut it - I’m just placing the icon in the 3D environment.

self.iconConsole.reparentTo(c.model)    # Reparent it to the object
self.iconConsole.setPos(0, -0.5, 10.5)  # Place it relative to the object
self.iconConsole.setBillboardPointEye() # Billboard effect
self.iconConsole.setDepthTest(False)    # Draw it on top of everything
self.iconConsole.setLightOff()          # Turn off lighting

Any help would be appreciated.

Here’s what I came up with; it does everything I needed. If anyone can think of a way to make it more efficient, let me know. It works well except for one thing - the icon stays near the object, but not in the same place. This is because as you look to the right, the icon moves across the 2D screen linearly, but the location of the object in the 3D space does not move linearly. I’ll try to post a video that shows what I mean more clearly.

    def updateIconPositions(self, consoles, player):
        for c in consoles:
            pPos = player.pNode.getPos() + player.camNode.getPos()    # Player's camera's global position
            cPos = c.getObjectivePos()      # Objective's global position
            
            diff = (cPos - pPos) #Vector from the camera to the obejctive
            
            # For finding the horizontal position of the objective on the screen
            #
            # BirdsEyeVector: Overhead X-Y vector from the player to the obejctive
            # LookingVector: Overhead X-Y vector of the direction the player is facing
            # BirdsEyeAngle: The horizontal angle
            
            # For finding the vertical position of the objective on the screen
            # We turn the 3D vectors (x, y, z) into 2D vectors (u, v), 
            # using the Z component of the 3D vectors as 'v' and he length of 
            # the X-Y components of the 3D vectors as 'u'
            
            # vertLookingVector: The 2D vector for the direction the player is facing
            # vertVector: The 2D vector from the player to the obejctive
            
            birdsEyeVector = Vec3(diff)
            birdsEyeVectorZ = birdsEyeVector.getZ()
            birdsEyeVector.setZ(0)
            diffLength = birdsEyeVector.length()
            birdsEyeVector.normalize()
            
            lookingVector = Vec3(player.lookingDir)
            lookingVectorZ = lookingVector.getZ()
            lookingVector.setZ(0)
            xyLength = lookingVector.length()
            lookingVector.normalize()
            
            birdsEyeAngle = birdsEyeVector.signedAngleDeg(lookingVector, Vec3(0, 0, 1))
            locationRatioX = birdsEyeAngle / (DEFAULT_FOV / 2)
            
            # Pin the icon to the left and right sides of the display region if
            # the objective is offscreen
            horizCutoff = 0.9
            if(locationRatioX > horizCutoff):
                locationRatioX = horizCutoff
            elif(locationRatioX < -horizCutoff):
                locationRatioX = -horizCutoff
            
            locationRatioX *= self.aspectRatio
            
            vertLookingVector = Vec2(xyLength, lookingVectorZ)
            vertVector = Vec2(diffLength, birdsEyeVectorZ)
            
            vertAngle = vertVector.signedAngleDeg(vertLookingVector)
            locationRatioZ = - vertAngle / ((DEFAULT_FOV / self.aspectRatio) / 2)
            
            # Pin the icon to the top and bottom sides of the display region if
            # the objective is offscreen
            vertCutoff = 0.9
            if(locationRatioZ > vertCutoff):
                locationRatioZ = vertCutoff
            elif(locationRatioZ < -vertCutoff):
                locationRatioZ = -vertCutoff
            
            self.iconConsole.setPos(locationRatioX, 0, locationRatioZ)
            
            # If the icon is near the crosshairs, make it less noticeable
            if( abs(birdsEyeAngle) < 5  and abs(vertAngle) < 5):
                self.iconConsole.setColor(Vec4(1, 1, 1, 0.2))
            else:
                self.iconConsole.setColor(Vec4(1, 1, 1, 0.7))

Interesting topic. Thanks for sharing :slight_smile: Thanks again if you happen to post any further updates on your progress.

//A

This video shows what I was talking about youtu.be/2SYl3XOYq2o?hd=1

You can see how the icon is always near the objective, but the way the icon moves in render2d doesn’t match how the objective moves in the screen.

this might help simplify what you’re doing:

[Mapping a point in render to aspect2d)