3D position to screen position, screen position to 3D position
-
Here's some code for translating 3D positions to screen positions and screen positions to 3D positions. It's a bit involved because it needs matrix math, but I've found it extremely useful.
Main functions:
// Projects world vector to screen postion. function worldPosToScreenPos(_worldPos, _camFwd, _camUp, _camPos, _camFov, _scrW, _scrH) var screenPos = {float_min, float_min} var nearClip = 0.25 var camPoint = worldVecToLocalVec(_worldPos, _camFwd, _camUp, _camPos) if camPoint.z >= nearClip then var height = 2 * tan(_camFov * 0.5) var width = height * (_scrW / _scrH) var posX = (camPoint.x / camPoint.z) / width var posY = (camPoint.y / camPoint.z) / height posX += (_scrW * 0.5) / _scrW posY += (_scrH * 0.5) / _scrH posX = (1 - posX) * _scrW posY = (1 - posY) * _scrH screenPos = {posX, posY} endif return screenPos // Projects screen position to world vector. function screenPosToWorldPos(_scrPos, _zDepth, _camFwd, _camUp, _camPos, _camFov) var posX = (_scrPos.x / gwidth() - 1) * -1 var posY = (_scrPos.y / gheight() - 1) * -1 posX -= (gwidth() * 0.5) / gwidth() posY -= (gheight() * 0.5) / gheight() var height = 2 * tan(_camFov * 0.5) var width = height * (gwidth() / gheight()) var pointX = posX * width * _zDepth var pointY = posY * height * _zDepth return localVecToWorldVec({pointX, pointY, _zDepth}, _camFwd, _camUp, _camPos)
Helper functions:
// Remaps a world vector as a local vector. function worldVecToLocalVec(_vec, _locFwd, _locUp, _locPos) _vec -= _locPos _locFwd = axisRotVecBy(_locFwd, _locUp, 90) var r = cross(_locFwd, _locUp) // Calculate inverse matrix var minFwd = {_locUp.y * r.z - r.y * _locUp.z, (_locUp.x * r.z - r.x * _locUp.z) * -1, _locUp.x * r.y - r.x * _locUp.y} var minUp = {(_locFwd.y * r.z - r.y * _locFwd.z) * -1, _locFwd.x * r.z - r.x * _locFwd.z, (_locFwd.x * r.y - r.x * _locFwd.y) * -1} var minR = {_locFwd.y * _locUp.z - _locUp.y * _locFwd.z, (_locFwd.x * _locUp.z - _locUp.x * _locFwd.z) * -1, _locFwd.x * _locUp.y - _locUp.x * _locFwd.y} var adjFwd = {minFwd.x, minUp.x, minR.x} var adjUp = {minFwd.y, minUp.y, minR.y} var adjR = {minFwd.z, minUp.z, minR.z} var det = 1 / (_locFwd.x * minFwd.x + _locUp.x * minUp.x + r.x * minR.x) var invFwd = det * adjFwd var invUp = det * adjUp var invR = det * adjR var newVec = changeVecSpace(_vec, invFwd, invUp, invR, _locPos * -1) newVec += _locPos return newVec // Remaps a local vector as a world vector. function localVecToWorldVec(_vec, _locFwd, _locUp, _locPos) var newVec = changeVecSpace(_vec, _locFwd, _locUp, cross(_locUp, _locFwd), {0, 0, 0}) newVec += _locPos return newVec // Rotates 3D vector _v around 3D vector _axis by _deg degrees. function axisRotVecBy(_v, _axis, _deg) // Euler-Rodrigues rotation formula var halfDeg = _deg / 2 var w = _axis * sin(halfDeg) var crossWV = cross(w, _v) return _v + 2 * cos(halfDeg) * crossWV + 2 * cross(w, crossWV) // Remaps a 3D vector to a different context. function changeVecSpace(_vec, _newFwd, _newUp, _newR, _newPos) var mapped = {0, 0 ,0} mapped.x = _vec.x * _newFwd.x + _vec.y * _newUp.x + _vec.z * _newR.x + _newPos.x mapped.y = _vec.x * _newFwd.y + _vec.y * _newUp.y + _vec.z * _newR.y + _newPos.y mapped.z = _vec.x * _newFwd.z + _vec.y * _newUp.z + _vec.z * _newR.z + _newPos.z return mapped
-
This is awesome, thanks for posting! I've tried to figure out how to do this myself on past occasions, and gave up-- my math skills aren't the best.