Log in to reply
 

How to make a ScreenToWorld



  • I'm using ScriptHookV.Net and I'd like to launch a beam from a specific pixel on the screen. I use a static camera.

    That is, I know the position of the cursor:
    Point mouse = System.Windows.Forms.Control.MousePosition;

    This is in 2d coordinates, I want to transform it into 3d coordinates with z = 0. And then send a raytrace to the direction of the camera.

    Point mouse = ScreenToWorld (new Vector3 (mouse.X, mouse.Y, 0));
             RaycastResult r = World.Raycast (mouseWorldCoords, mouseWorldCoords + 10 * GameplayCamera.Direction, IntersectOptions.Everything);

    How can I do this?

    I can not use the camera's position only because I lose the position of the cursor.


  • MODERATOR

    Modified from: https://gtaforums.com/topic/802220-screen-to-world-coordinates/?do=findComment&comment=1067790512

    void Main()
            {
                //Function.Call((Hash)0xAAE7CE1D63167423);
                Game.EnableControlThisFrame(0, GTA.Control.CursorX);
                Game.EnableControlThisFrame(0, GTA.Control.CursorY);
                float x = Game.GetControlNormal(0, GTA.Control.CursorX);
                float y = Game.GetControlNormal(0, GTA.Control.CursorY);
                Function.Call(Hash.DRAW_RECT, x, y, 0.01f, 0.01f, 0, 0, 255, 255, false);
    
                Vector3 dir;
                Vector3 cam3DPos = ScreenRelToWorld(GameplayCamera.Position, GameplayCamera.Rotation, new Vector2(x, y), out dir);
                //World.DrawMarker(MarkerType.DebugSphere, cam3DPos, Vector3.Zero, Vector3.Zero, new Vector3(0.01f, 0.01f, 0.01f), Color.Red);
                
                RaycastResult r = World.Raycast(cam3DPos, dir, 50f, IntersectOptions.Everything, Game.Player.Character);
                if (r.DitHitAnything)
                {
                    World.DrawMarker(MarkerType.DebugSphere, r.HitCoords, Vector3.Zero, Vector3.Zero, new Vector3(0.2f, 0.2f, 0.2f), Color.Blue);
                }
            }
    
            public static bool World3DToScreen2D(Vector3 pos, out Vector2 result)
            {
                var x2dp = new OutputArgument();
                var y2dp = new OutputArgument();
    
                bool success = Function.Call<bool>((Hash)0x34E82F05DF2974F5, pos.X, pos.Y, pos.Z, x2dp, y2dp); //  GET_SCREEN_COORD_FROM_WORLD_COORD and previously, _WORLD3D_TO_SCREEN2D
                result = new Vector2(x2dp.GetResult<float>(), y2dp.GetResult<float>());
                return success;
            }
    
            public static Vector3 ScreenRelToWorld(Vector3 camPos, Vector3 camRot, Vector2 coord, out Vector3 forwardDirection)
            {
                var camForward = RotationToDirection(camRot);
                var rotUp = camRot + new Vector3(1, 0, 0);
                var rotDown = camRot + new Vector3(-1, 0, 0);
                var rotLeft = camRot + new Vector3(0, 0, -1);
                var rotRight = camRot + new Vector3(0, 0, 1);
    
                var camRight = RotationToDirection(rotRight) - RotationToDirection(rotLeft);
                var camUp = RotationToDirection(rotUp) - RotationToDirection(rotDown);
    
                var rollRad = -DegToRad(camRot.Y);
    
                var camRightRoll = camRight * (float)Math.Cos(rollRad) - camUp * (float)Math.Sin(rollRad);
                var camUpRoll = camRight * (float)Math.Sin(rollRad) + camUp * (float)Math.Cos(rollRad);
    
                var point3D = camPos + camForward * 1.0f + camRightRoll + camUpRoll;
                Vector2 point2D;
                if (!World3DToScreen2D(point3D, out point2D))
                {
                    forwardDirection = camForward;
                    return camPos + camForward * 1.0f;
                }
                var point3DZero = camPos + camForward * 1.0f;
                Vector2 point2DZero;
                if (!World3DToScreen2D(point3DZero, out point2DZero))
                {
                    forwardDirection = camForward;
                    return camPos + camForward * 1.0f;
                }
    
                const double eps = 0.001;
                if (Math.Abs(point2D.X - point2DZero.X) < eps || Math.Abs(point2D.Y - point2DZero.Y) < eps)
                {
                    forwardDirection = camForward;
                    return camPos + camForward * 1.0f;
                }
                var scaleX = (coord.X - point2DZero.X) / (point2D.X - point2DZero.X);
                var scaleY = (coord.Y - point2DZero.Y) / (point2D.Y - point2DZero.Y);
                var point3Dret = camPos + camForward * 1.0f + camRightRoll * scaleX + camUpRoll * scaleY;
                forwardDirection = camForward + camRightRoll * scaleX + camUpRoll * scaleY;
                return point3Dret;
            }
    
            public static float DegToRad(float _deg)
            {
                double Radian = (Math.PI / 180) * _deg;
                return (float)Radian;
            }
    
            public static Vector3 RotationToDirection(Vector3 rotation)
            {
                var z = DegToRad(rotation.Z);
                var x = DegToRad(rotation.X);
                var num = Math.Abs(Math.Cos(x));
                return new Vector3
                {
                    X = (float)(-Math.Sin(z) * num),
                    Y = (float)(Math.Cos(z) * num),
                    Z = (float)Math.Sin(x)
                };
            }
    


  • Thank you very much still here! In the end I found the same post and also made some changes. In fact I think that the ideal would be to do a pull request to add it to the library itself.

    It also required asking even if it does not have much to do. I'm trying to use this beam to apply blows to the car.

    I got it using the function APPLY_FORCE_TO_ENTITY (in this case I only use the address). The problem is that the only thing he does is apply a force but it does not harm him.

    I've also tried SET_VEHICLE_DAMAGE which was perfect for this purpose because I can easily find the position relative to the car (GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS). The problem is that this function does not alter anything in the car, I tried different radios, and damage and nothing ...


  • MODERATOR

    @totolia from my testing, this function requires rather large values for some reason.

    void SetVehicleVisualDamage(Vehicle v, Vector3 worldCoord, float visualDamageAmount = 200f, float radiusOfDamage = 250f, bool p6 = true)
            {
                Vector3 offset = v.GetOffsetFromWorldCoords(worldCoord);
                Function.Call(Hash.SET_VEHICLE_DAMAGE, v, offset.X, offset.Y, offset.Z, visualDamageAmount, radiusOfDamage, p6);
            }
    

    Try something like SetVehicleVisualDamage(veh, Game.Player.Character.Position, 8600f, 800f), it seems to work with high values like this.
    The parameter names are just something I made up. visualDamageAmount could be more like the depth of damage (deeper dents).



  • You're right! Now it works a lot of thanks, I did not know how to do it. Thank you!

    Do you know if there is a way to know which part of the car is correct? Something like: front, wheel, rear bumper, etc ... I know that internally you should know why you go to a mechanic you know where the blow occurred but the nose as you get from the position of the hit the object....


  • MODERATOR

    If you mean you want to create the damage at a specific part of the car, you would need to get the offset of the vehicle bone related to the part.

    GET_WORLD_POSITION_OF_ENTITY_BONE
    vehicle.GetOffsetFromWorldCoords

    Bone indexes: https://pastebin.com/D7JMnX1g (search for Vehicle Bones)



  • @Jitnaught said in How to make a ScreenToWorld:

    GetOffsetFromWorldCoords

    Similar but on the contrary, I want to apply a damage using the mouse and know what zone of the car has occurred. But I think that with what you've passed me, I can through Euclidean distance. Know where it is! Many thanks


Log in to reply
 

Looks like your connection to GTA5-Mods.com Forums was lost, please wait while we try to reconnect.