I’m working on a drag and drop script for the game engine. We need it
to move objects around on the screen. I have handled all of the
collision math and logic for moving objects around but there is one
last thing that I haven’t been able to figure out…
How do we convert Screen (window) Coordinates to World Coordinates
And
How do we convert World Coordinates to Screen Coordinates
(No, I’m not interested in using a ray sensor. All of the movable
objects are read in from external libraries. We haven’t been able to
figure out how to attach a logic brick to them using a script)
I’ve been able to piece together most of it from Letter Rips code and
from the MouseOverSensor source code. One thing that I know I haven’t
been able to find is how to properly convert the depth coordinate into
a normalized screen coordinate.
I’ve included a simple script. Place a mouse click sensor on an object
and connect it to a python controller for this script. In game mode,
when you click on the screen, the script will output conversion of
the screen coordinates to the world coordinates and world coordinates
(of the object) to screen coordinates.
There are two behaviors:
Orthogonal views work ok, except for the depth values. I think I just
need help with the Normalized device coordinates to fix that
Perspective works well at the center of the screen and then errors
increase the further away from the center of the screen you get.
import Blender
from Blender import Window, Mathutils
from Blender.Mathutils import *
import GameLogic
import Rasterizer
import math
#dehomonogizes a vector
def dehom(v):
ret = Vector(v[0]/v[3],v[1]/v[3],v[2]/v[3])
return ret
#takes the inverse of a matrix and returns it
def inverseMatrix(m):
ret = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
det = m.determinant()
for i in range(4):
for j in range(4):
temp = Matrix([0,0,0],[0,0,0],[0,0,0])
col = 0
for x in range(4):
if x != i:
row = 0
for y in range(4):
if y != j:
temp[col][row] = m[x][y]
row = row +1
col = col +1
tdet = temp.determinant()
total = i+j
if (total % 2):
sign = -1
else:
sign = 1
#i and j are flipped
ret[j][i] = (sign * tdet) / det
return ret
#move a 4x4 list into a matrix
def M2M(M):
return (Matrix(M[0],M[1],M[2],M[3]))
#move a 3x3 list into a matrix
def M2MD3(M):
return (Matrix(M[0],M[1],M[2]))
#transforms the world cordinates to screen (window) coordinates
def transWC2SC(wc_x,wc_y,wc_z):
#clip taken from letter rip's code--have to assume that the vals are ok
for win3d in Window.GetScreenInfo(Window.Types.VIEW3D):
# we search all 3dwins for the one containing the point
#(screen_x, screen_y) (could be the mousecoords for example)
win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
# calculate a few geometric extents for this window
mid_x = (win_max_x + win_min_x)/2.0
mid_y = (win_max_y + win_min_y)/2.0
width = (win_max_x - win_min_x + 1.0)
height = (win_max_y - win_min_y + 1.0)
pm = Window.GetPerspMatrix()
coords = Vector(wc_x,wc_y,wc_z,1.0)
#print "object location coords", coords
val = coords*pm
#now dehomonigize
val = dehom(val)
#convert from Normalized Device Coordinates to screen coordinates
val[0] = mid_x+ val[0]*width/2.0
val[1] = mid_y + val[1]*height/2.0
val[2] = (val[2]+1)/2 #This isn't right, but It is the only thing I've seen
#that seems to works sometimes
return val
def transSC2WC(screen_x,screen_y,screen_z):
found = 0;
#clip taken from letter rips code
for win3d in Window.GetScreenInfo(Window.Types.VIEW3D):
# we search all 3dwins for the one containing the point
#(screen_x, screen_y) (could be the mousecoords for example)
win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
# calculate a few geometric extents for this window
mid_x = (win_max_x + win_min_x)/2.0
mid_y = (win_max_y + win_min_y)/2.0
width = (win_max_x - win_min_x + 1.0)
height = (win_max_y - win_min_y + 1.0)
# check if screencoords (screen_x, screen_y) are within the 3dwin
if (win_max_x > screen_x > win_min_x) and ( win_max_y > screen_y > win_min_y):
found = 1
break
if(not found):
print "Not Found!"
return 0, 0, 0
NDC = Vector( 2* ( screen_x - mid_x)/ width,
2*(screen_y - mid_y)/height,
2*screen_z - 1, #can't believe this seems to work
1.0)
persp = Window.GetPerspMatrix()
invpersp = inverseMatrix(persp)
coords = NDC
newcoords = coords*invpersp #This Works for orthongonal!
newcoords = dehom(newcoords)
return newcoords
#found this in the documentation, using it to find the depth
#for converting screen coordinates to window coordinates
def getObjDepth(pos):
depth = pos[0]*cam.world_to_camera[2][0]
depth = depth + pos[1]*cam.world_to_camera[2][1]
depth = depth + pos[2]*cam.world_to_camera[2][2]
depth = depth + cam.world_to_camera[2][3]
return depth
#distance between two points (in world coordinates)
def distanceCamToObj(pos,cpos):
dist = [cpos[0]-pos[0],cpos[1]-pos[1],cpos[2]-pos[2]]
ret = dist[0]*dist[0]+dist[1]*dist[1]+dist[2]*dist[2]
return math.sqrt(ret)
def main():
screen_x, screen_y=Window.GetMouseCoords()
pos = owner.getPosition()
depth = getObjDepth(pos)
print "
Screen Coords", screen_x,screen_y,-depth
print "Translate Position(World) to Screen Coords"
print transWC2SC(pos[0],pos[1],pos[2]),"
"
print "Position Coords",pos
print "Translate Screen Coords to World Coords"
print transSC2WC(screen_x,screen_y,-depth)
#define globals
controller=GameLogic.getCurrentController()
owner = controller.getOwner()
scene = GameLogic.getCurrentScene()
cam = scene.active_camera
#Initialize the script
try:
owner.init
except AttributeError:
owner.init = 1
Rasterizer.showMouse(1)
main()
I plan on placing the result on the forums for the next person that
askes this question. Lets put this one to rest!
Centre Prof