Author Topic: Converting a world position into a screen coordinate  (Read 2346 times)

I'm attempting to figure out the correct methodology for converting a world position into a screen coordinate. Hence the topic title. However, this code doesn't work 100% of the time and I haven't figured out how to get rid of each of the three problems.

  • Deflection from accuracy except at the center axes and edges of the screen.
  • Incredible inaccuracy as one looks further up/down at it.
  • Doesn't rotate if the player is in a vehicle.

Code: (World Position to Screen Coordinate) [Select]
function posToCoord(%pos)
{
if(!isObject(%cl = ServerConnection) || !isObject(%pl = %cl.getControlObject()))
return;
%pi = 3.14159265;
%eye = %pl.getMuzzleVector(0);   //Not actually the eye vector but you get the idea
%raw = %pl.getPosition();
if(%pl.isCrouched())
%loc = vectorAdd(%raw, "0 0 0.5");
else
%loc = vectorAdd(%raw, "0 0 1.325");
%dir = vectorNormalize(vectorSub(%pos, %loc));
%eyeX = mAtan(getWord(%eye, 0), getWord(%eye, 1));
%dirX = mAtan(getWord(%dir, 0), getWord(%dir, 1));
%eyeLen = mSqrt(mPow(getWord(%eye, 0), 2) + mPow(getWord(%eye, 1), 2));
%dirLen = mSqrt(mPow(getWord(%dir, 0), 2) + mPow(getWord(%dir, 1), 2));
%eyeY = mAtan(%eyeLen, getWord(%eye, 2));
%dirY = mAtan(%dirLen, getWord(%dir, 2));
%yDiff = %eyeY - %dirY;
%xDiff = (%eyeX - %dirX);// * mSin(%eyeY);
if(%xDiff > %pi)
%xDiff -= 2 * %pi;
if(%xDiff < -%pi)
%xDiff += 2 * %pi;
%xRange = 0.785;   //Max x deviation for a point which is still on the screen
%yRange = 0.655;   // "  y     "      "  "   "     "    "   "    "  "     "
%extent = getRes();
%halfX = getWord(%extent, 0) / 2;
%halfY = getWord(%extent, 1) / 2;
%xVal = ((%xDiff / %xRange) * -%halfX) + %halfX;// + (mSin(-%xDiff * 2) * %halfX * 0.2);
%yVal = ((%yDiff / %yRange) * -%halfY) + %halfY;// + (mSin(-%yDiff * 2) * %halfY * 0.2);
return mFloatLength(%xVal, 0) SPC mFloatLength(%yVal, 0);
}


Any of you got any ideas what I'm doing (horribly horribly) wrong?

Based on the silence I'm assuming that no one has any suggestions for me to try.

Based on the silence I'm assuming that no one has any suggestions for me to try.

Well the issue is that its a ton of stacked math with abstract variable names so its a  ton of work just to get to understand it.
I would suggest taking a mod like aimbot that can aim your player at a point, then doing some math with the FOV to find offsets from the center, then you will need to do something with the resolution.  Idk how yours exactly works, I don't see anything with the field of view and I'd imagine that's pretty important.

I don't see anything with the field of view and I'd imagine that's pretty important.

I was planning on adding that once I got the dots to stay in the right screen positions with the default FOV.

Screen Variable -
What do you mean?
like, it showing up as an object where that position is, or like a coordinate?
Spawning something at that location?


Someone else I know is trying to do the exact same thing, except his only bug is that it gets inaccurate based on the x and y resolution, in the shape of a sine wave.

Someone else I know is trying to do the exact same thing, except his only bug is that it gets inaccurate based on the x and y resolution, in the shape of a sine wave.

I noticed that too. The offset from the center/edges is definitely a sine wave, but I don't know the correct amount to put the point back over its real-world equivalent. Besides, that's not the only problem.

If someone could explain how the screen display dynamic actually works I could remake/check the math code pretty easily

I don't think I'd be able to help here, sorry. I'd recommend googling it and seeing if someone found an implimentation for a different game engine that you could adapt.

Quote from: Steam
Jailbot - RIP HPRC: post there say slick knows how and he'll get some code together
kk

I was playing with something like this awhile ago, see if you can adapt this code:

Code: [Select]
...

%obj   = %info.obj;
%gui   = %info.gui;
%name  = %obj.getShapeName();
%posa  = %self.getTransform();
%posb  = %obj.getTransform();

%fv    = %self.getMuzzleVector(0);
%yp    = getYawPitch(%fv,%posa,%posb);
%yaw   = getWord(%yp,0);
%pitch = getWord(%yp,1);

...

%halfx = getWord($Pref::Video::Resolution,0) / 2;
%halfy = getWord($Pref::Video::Resolution,1) / 2;
%fovx  = mDegToRad($CameraFov / 2);
%fovy  = mATan(mTan(%fovx) * %halfy / %halfx,1);

if(%yaw   >  %fovx) %yaw   =  %fovx;
if(%yaw   < -%fovx) %yaw   = -%fovx;
if(%pitch >  %fovy) %pitch =  %fovy;
if(%pitch < -%fovy) %pitch = -%fovy;

%ratx  = mTan(%yaw)   * mCos(%fovx) / mSin(%fovx);
%raty  = mTan(%pitch) * mCos(%fovy) / mSin(%fovy);

%x = mFloatLength(%ratx * %halfx + %halfx - getWord(%gui.extent,0) / 2,0);
%y = mFloatLength(%raty * %halfy + %halfy - getWord(%gui.extent,1) / 2,0);

%gui.position = %x SPC %y;
}

function getYawPitch(%fv,%posa,%posb)
{
%x  = getWord(%fv,0);
%y  = getWord(%fv,1);
%vv = vectorNormalize(vectorSub(%posb,getWords(%posa,0,2)));
%xx = getWord(%vv,0);
%yy = getWord(%vv,1);

%yaw   = mATan(%xx,%yy) - mATan(%x,%y);
%pitch = mATan(getWord(%fv,2),mSqrt(%x  * %x  + %y  * %y )) -
         mATan(getWord(%vv,2),mSqrt(%xx * %xx + %yy * %yy));

if(%yaw   >  3.14159) %yaw   = -6.28318 + %yaw;
if(%yaw   < -3.14159) %yaw   =  6.28318 + %yaw;
if(%pitch >  3.14159) %pitch = -6.28318 + %pitch;
if(%pitch < -3.14159) %pitch =  6.28318 + %pitch;

return %yaw SPC %pitch;
}

Assume (I might have missed a couple but you shouldn't copypasta this anyway):

Quote
%self is the player object viewing the other objects
%info was an SO containing the other object's ID (.obj) and a swatch (.gui)

Note that there were inaccuracies in the far corners, so you could improve on that.

This is the correct way to do it:
Code: [Select]
function worldToScreen(%eyeTransform, %worldPosition, %screenExtent, %cameraFoV)
{
%offset = MatrixMulVector("0 0 0" SPC getWords(%eyeTransform, 3, 5) SPC -1 * getWord(%eyeTransform, 6), VectorSub(%worldPosition, %eyeTransform));
%x = getWord(%offset, 0);
%y = getWord(%offset, 1);
%z = getWord(%offset, 2);

%fovFactor = %y * (%cameraFoV != 90 ? mTan(%cameraFoV * $pi / 360) : 1);
%screenWidth = getWord(%screenExtent, 0);
%screenHeight = getWord(%screenExtent, 1);

%screenX = ((%x / %fovFactor) + 1) / 2 * %screenWidth;
%screenY = ((%z / %fovFactor * %screenWidth) - %screenHeight) / -2;

return %screenX SPC %screenY;
}

If you're trying to do this client-side only, you need to calculate the eye transform.  Of course, you shouldn't be messing with this kind of thing in the first place if you do not fully understand the math behind it.  Otherwise, you end up wasting time trying to experiment and guess how to do it.
« Last Edit: September 29, 2012, 02:05:56 AM by Nugga T »


If you're trying to do this client-side only, you need to calculate the eye transform.

Bump
made a little resource for approximating your eye point and focus point client sided.
It appears to have some accuracy problems at distances above like 300
If someone has a better way, I would be delighted to hear about it.

Code: [Select]
function getfocuspos()
{
%dist = getfocusdistance();
%eye = getmyeyepoint();
%vec = serverconnection.getcontrolobject().getmuzzlevector(0);
%vec = vectorscale(%vec, %dist);
return vectoradd(%eye, %vec);
}

function getmyeyepoint()
{
%player = serverconnection.getcontrolobject();

if(!isobject(%player))
return "0 0 0";
%pos = %player.getposition();
%vec = %player.getforwardvector();
%scale = %player.getscale();
%x = getword(%pos, 0) + (getword(%vec, 0)*0.14 + 0.002)*getword(%scale, 0); //me no likey
%y = getword(%pos, 1) + (getword(%vec, 1)*0.14 + 0.002)*getword(%scale, 1);
%z = getword(%pos, 2) + (getword(%player.getdatablock().boundingbox, 2)/4.92 + $iamcrouching*1.53 + 0.002)*getword(%scale, 2); //optimized for standard blockhead, good luck otherwise
return %x SPC %y SPC %z;
}

(requires a package to create the $iamcrouching variable)

This should probably help some.  I realize there is an $mvtrigger3 or something, this isn't the final version.  Look in TMBI if this doesn't do quite what is needed.

« Last Edit: September 29, 2012, 02:30:15 AM by Port »