Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pyglet HUD text location / scaling

Background

I have a game with a HUD I based on this example

I have a pyglet application with two gluOrtho2D views. One which is mapped 1/1 to the screen, which acts as my HUD. One which is mapped to the game world, so it scales.

Problem

When a player in the game world is rendered in a certain location, I want to display the name of this player in the HUD. But the HUD knows nothing about the world objects rendering positions, and the world knows nothing about the scaling of the screen.

Question

Is there a way to find out where something gets rendered on the screen, or is it possible to find out or simply counter-act the current scaling set in the context?

Minimal example code

Imports

from pyglet import clock, window, font
from pyglet.gl import *

Camera class that sorts out the difference between the HUD and the 2D world

class Camera(object):

    def __init__(self, win):
        self.win = win

    def project_world(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluOrtho2D(-500, 1000, -500, 500)

    def project_hud(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluOrtho2D(0, self.win.width, 0, self.win.height)

The HUD that contains everything that is rendered without a viewpoint.

class Hud(object):

    def __init__(self, win: window.Window):
        self.fps = clock.ClockDisplay()
        self.helv = font.load('Helvetica', 12.0)
        self.text = font.Text(self.helv, "This text does not scale", \
                              x=0, y=0, color=(1, 1, 1, 0.5))

    def draw(self):
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.text.draw()

The world which contains everything that is rendered from the player viewpoint

class World(object):

    def __init__(self):
        self.helv = font.load('Helvetica', 12.0)
        self.text = font.Text(self.helv, "This text should not scale", \
                              x=0, y=0, color=(1, 1, 1, 0.5))

    def draw(self):
        glClear(GL_COLOR_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.text.draw()

The application that runs the whole deal

class App(object):

    def __init__(self):
        self.win = window.Window(fullscreen=False, vsync=True, width=800, height=600)
        self.world = World()
        self.camera = Camera(self.win)
        self.hud = Hud(self.win)
        clock.set_fps_limit(60)

    def run(self):
        while not self.win.has_exit:
            self.win.dispatch_events()

            self.camera.project_world()
            self.world.draw()

            self.camera.project_hud()
            self.hud.draw()

            clock.tick()
            self.win.flip()

app = App()
app.run()

Uglyhack

For now I have solved it by ugly hacking it. Sensitive readers please do not keep on reading.

I now calculate the ratio and origo of the camera in the world and then I link the world to the HUD, so the HUD can read the attributes and calculate the position for the text itself.

This is of course Spaghetti code anti pattern which I do not want.

like image 889
firelynx Avatar asked Jul 16 '16 19:07

firelynx


1 Answers

I am not a python programmer but I think i understand enough of the opengl part of the problem so I will attempt to answer. Please forgive my syntax.

I believe you want a 2D position that represents a 3D point in your scene. You want to know the position in 2D so you can draw some text there. You have the modelview as well as the projection matrices for the 3D world. getting the 2D position is a simple matter of matrix multiplication.

assuming my player position is p(1.0, 1.0, 1.0)

import numpy as np
import pyglet 

#transform point to camera or eye space
posV = np.array([1.0,1.0,1.0,1.0])
pyglet.gl.glMatrixMode(pyglet.gl.GL_MODELVIEW)
mvmat = (pyglet.gl.GLdouble * 16)()
pyglet.gl.glGetDoublev(pyglet.gl.GL_MODELVIEW_MATRIX, mvmat)
pyglet.gl.glLoadIdentity()
npmvmat = np.array( mvmat ).reshape((4,4))
eyeV = np.dot(npmvmat, posV)

#transform point to NDC
pyglet.gl.glMatrixMode(pyglet.gl.GL_PROJECTION)
projmat = (pyglet.gl.GLdouble * 16)()
pyglet.gl.glGetDoublev(pyglet.gl.GL_PROJECTION_MATRIX, projmat )
pyglet.gl.glLoadIdentity()
npprojmat = np.array( projmat ).reshape((4,4))
screenV = np.dot(npprojmat, eyeV)

#w-divide - convert from NDC
screenV[0] = screenV[0] / screenV[3]
screenV[1] = screenV[1] / screenV[3]

#[-1,1] shift to [0,1]
x = 0.5 + (0.5 * screenV[0])
y = 0.5 + (0.5 * screenV[1])    

#get a position on the viewport change to your viewport size
width = 1920
height = 1080
x = x * width
y = y * height

print (x,y)

finally your x and y will contain the 2D projection of the 3D point.

Note : make sure you position glGetDoublev() calls after your matrices have been setup. store them in a variable and use it to project to 2D.

hope this helps

like image 80
Harish Avatar answered Sep 20 '22 21:09

Harish