Hi everybody–
So, at various many places in these forums people are trying to figure out how to get from coordinates in 3D space to plain old iPad-screen x and y. At none of them can I actually find a solution for it–at least not one I’ve been able to implement myself.
I’ve been at it for days. My head is sore from the wall I’ve been banging it on. Here’s the code I’ve built trying to understand all this, if somebody can help me make it work right, may a thousand blessings comfort thee. It should be pretty clear what I’m trying to do once you paste it into Codea and play with it a little.
--# Main
-- Analysis Lab
-- by UberGoober
-- I'm trying to figure out how to convert from 3D coordinates to 2D. This project puts a red square (I call it a box in the code, but it's really a square) at 0,0 then transforms it according to parameters dictated by parameter widgets. It then tries to figure out the absolute screen location of the square's upper right corner, according to two methods: one suggested in a thread begun by pat(shown as a green circle), one suggested in a thread by spaceMonkey(shown as a blue circle). It almost works for x and y transforms, but not camera and z transforms and angle and perspective--oy.
saveProjectInfo( "Description", "Trying to figure out $&?!* normalized device coordinates." )
function setup()
setUpLocationTracking()
end
function draw()
background(53, 30, 69, 255)
applyTracking()
end
--# Position
function setUpLocationTracking()
--a bunch of stuff to fiddle with and watch
parameter.watch("allMatrix")
parameter.watch("duoMatrix")
parameter.watch("patCoords")
parameter.watch("offset")
parameter.integer("rotateBoxByAngle", 0, 360, 0)
parameter.integer("fieldOfView", 1, 200, 45)
parameter.integer("eyeAndLookX", -WIDTH, WIDTH, 0)
parameter.integer("moveBoxByX", -2000, 2000, 0)
parameter.integer("moveBoxByY", -2000, 2000, 0)
parameter.integer("moveBoxByZ", -2000, 2000, 0)
parameter.integer("rotateBoxX", 0, 1, 0)
parameter.integer("rotateBoxY", 0, 1, 0)
parameter.integer("rotateBoxZ", 0, 1, 1)
patCoords = vec2()
allMatrix = matrix()
watchVector = vec2(20,20) --i.e. *normally* the corner of the red box
--a bunch of functions to do what needs to be done, applied in the right order later:
--draw best guess as to screen x, y of the red box's corner onto the screen.
--this isnt really the best guess at this point
function printInfoText()
pushStyle()
textMode(CORNER)
textWrapWidth(0)
font("Futura-MediumItalic")
fontSize(HEIGHT/35)
local textIndentX, textIndentY = HEIGHT/15, HEIGHT-(HEIGHT/15)
infoText = string.format("best guess at screen location of red box UR corner: %d,%d",
SMCoords.x, SMCoords.y)
fill(21, 24, 34, 255)
text(infoText,textIndentX+2, textIndentY-2)
fill(248, 248, 248, 255)
text(infoText,textIndentX, textIndentY)
popStyle()
end
--draw the red box, 40 pixels square, centered over 0,0, (to be transformed by parameters)
function drawTrackingBoxAt(x,y)
pushStyle()
strokeWidth(6)
stroke(255, 7, 0, 255)
rect(x-21,y-21,44,44)
stroke(255, 255, 255, 255)
fill(255, 7, 0, 255)
rect(x-19,y-19,40,40)
fill(255, 255, 255, 255)
rect(x-4,y-4,10,10)
popStyle()
end
--apply the parameters set by the fiddlies
function applyParameters()
perspective(fieldOfView,WIDTH/HEIGHT,0.1,0)
camera(eyeAndLookX,0,1250,eyeAndLookX,0,0)
translate(moveBoxByX,moveBoxByY,moveBoxByZ)
rotate(rotateBoxByAngle,rotateBoxX,rotateBoxY,rotateBoxZ)
end
--undo all the fiddlies
function cancelPerspective()
viewMatrix(matrix())
ortho()
end
--apply the conversion suggested in pat's thread:
function patConversion(vector)
local nV = vec3()
local vectorX, vectorY, vectorZ = vector.x, vector.y, 0
if not vector.z == nil then vectorZ = vector.z end
duoMatrix = modelMatrix()*viewMatrix()
nV.x = vectorX*duoMatrix[1] + vectorY*duoMatrix[5] + vectorZ*duoMatrix[9] + duoMatrix[13]
nV.y = vectorX*duoMatrix[2] + vectorY*duoMatrix[6] + vectorZ*duoMatrix[10] + duoMatrix[14]
nV.z = vectorX*duoMatrix[3] + vectorY*duoMatrix[7] + vectorZ*duoMatrix[11] + duoMatrix[15]
return nV
end
--apply the conversion suggested by spaceMonkey:
function spaceMonkeyConversion(vector)
--trying to correct for projection matrix, which puts 0,0 at center screen and other goofy
--stuff, so to match it we gotta do like so:
allMatrix = modelMatrix()*viewMatrix()*projectionMatrix()
offset = vec3()
if allMatrix[15] ~= 0 then
offset = vec2((allMatrix[13]/allMatrix[16]+1)*WIDTH/2,
(allMatrix[14]/allMatrix[16]+1)*HEIGHT/2)
end
offset.x = vector.x + offset.x
offset.y = vector.y + offset.y
return offset
end
--put it all together:
function applyTracking()
pushMatrix()
applyParameters() --do what the fiddlies say
drawTrackingBoxAt(0,0) --draw the box, under the influence of the fiddlies
patCoords = patConversion(watchVector) --get the x,y predicted by pat's method
SMCoords = spaceMonkeyConversion(watchVector) --get the x,y from spaceMonkey's method
cancelPerspective() --undo all the fiddlies' stuff
popMatrix()
fill(0, 255, 38, 255)
ellipse(patCoords.x,patCoords.y,21,21) --draw a green circle at the x,y from pat
fill(0, 15, 255, 255)
ellipse(SMCoords.x, SMCoords.y,21,21) --draw a blue circle at the x,y from spacemonkey
printInfoText() --
end
end