[SOLVED] Camera X, Y, Z query

First, you can get the distance between the camera and a ship, right?
Then, get the distances between the camera and 4 ships whose coordinates are known, with some calculates, tada, you have the coordinate of the camera.

2 Likes

Yeah, I saw the Camera_GetDistanceToSobGroup() function but didn’t think of that. Well done !

AXIS_DISTANCE = 100;

function OnInit()
    Volume_AddSphere("Volume_Map0", {0, 0, 0}, 0.01)
    Volume_AddSphere("Volume_MapX", {AXIS_DISTANCE, 0, 0}, 0.01)
    Volume_AddSphere("Volume_MapY", {0, AXIS_DISTANCE, 0}, 0.01)
    Volume_AddSphere("Volume_MapZ", {0, 0, AXIS_DISTANCE}, 0.01)

    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_0", "SobGroup_Map0", "Volume_Map0")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_X", "SobGroup_MapX", "Volume_MapX")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Y", "SobGroup_MapY", "Volume_MapY")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Z", "SobGroup_MapZ", "Volume_MapZ")
    
    SobGroup_SetInvulnerability("SobGroup_Map0", 1)
    SobGroup_SetInvulnerability("SobGroup_MapX", 1)
    SobGroup_SetInvulnerability("SobGroup_MapY", 1)
    SobGroup_SetInvulnerability("SobGroup_MapZ", 1)
end

function Camera_GetPosition()
    local a = Camera_GetDistanceToSobGroup("SobGroup_Map0")
    local b = Camera_GetDistanceToSobGroup("SobGroup_MapX")
    local c = Camera_GetDistanceToSobGroup("SobGroup_MapY")
    local d = Camera_GetDistanceToSobGroup("SobGroup_MapZ")
    
    -- (1) a^2 = x^2 + y^2 + z^2
    -- (2) b^2 = (x-AXIS_DISTANCE)^2 + y^2 + z^2
    -- (3) c^2 = x^2 + (y-AXIS_DISTANCE)^2 + z^2
    -- (4) d^2 = x^2 + y^2 + (z-AXIS_DISTANCE)^2
    
    -- solve for x^2 for future substitutions:
    -- (5) x^2 = a^2 - y^2 - z^2
    
    -- substituting x^2 into (4) gives us:  d^2 = a^2 - z^2 + (z-AXIS_DISTANCE)^2 (the y-term cancels out)
    -- expanding the z term:                d^2 = a^2 - z^2 + z^2 - 2*AXIS_DISTANCE*z + AXIS_DISTANCE*AXIS_DISTANCE
    -- simplifying:                         d^2 = a^2 - 2*AXIS_DISTANCE*z + AXIS_DISTANCE*AXIS_DISTANCE
    -- and solving for z:
    local z = AXIS_DISTANCE*AXIS_DISTANCE + a*a - d*d;
    
    -- substituting x^2 into (3) gives us:  c^2 = a^2 - y^2 + (y-AXIS_DISTANCE)^2 (the z-term cancels out)
    -- expanding the y term:                c^2 = a^2 - y^2 + y^2 - 2*AXIS_DISTANCE*y + AXIS_DISTANCE*AXIS_DISTANCE
    -- simplifying:                         c^2 = a^2 - 2*AXIS_DISTANCE*y + AXIS_DISTANCE*AXIS_DISTANCE
    -- and solving for y:
    local y = AXIS_DISTANCE*AXIS_DISTANCE + a*a - c*c;
    
    -- now plug y and z into (5):
    local x = sqrt(a - y*y - z*z);
    
    -- sqrt actually gives us two values, so now we check with (2)
    if ((b * b) ~= ((x-AXIS_DISTANCE)*(x-AXIS_DISTANCE) + y*y + z*z)) then
        x = -x;
    end
    
    return {x, y, z};
end

Hgn_StaticProbe is just what I decided to call the ship. You can use any ship, just make a copy of it, and set the speed/thruster values in the ship file to 0, and disable movement.

(I haven’t actually tested this code to see if it works…)

Do yo have a GUI button to trigger this command? What about the direction the camera is facing?

Whoops! Pretty big calculation error:

local z = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - d*d) / (2*AXIS_DISTANCE);
local y = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - c*c) / (2*AXIS_DISTANCE);

Forgot to divide…


And:

local x = sqrt(a*a - y*y - z*z);

Forgot I switched from saying a was pre-squared to saying a was not squared.


And figured out how to say “screw you” to the sqrt function (was getting the wrong x value)

local x = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - b*b) / (2*AXIS_DISTANCE);

Complete fixed code:

data:ship/hgn_staticprobe/ copied hgn_probe folder and re-named files.
data:ship/hgn_staticprobe/hgn_staticprobe.ship – modified lines:

-- just in case SobGroup_SetInvulnerability fails
NewShipType.maxhealth=getShipNum(NewShipType, "maxhealth", 100000)
NewShipType.regentime=1
NewShipType.minRegenTime=1

...

NewShipType.thrusterMaxSpeed=0
NewShipType.mainEngineMaxSpeed=0
NewShipType.rotationMaxSpeed=0

...

-- changing these didn't actually seem to affect the sway?
NewShipType.swayUpdateTime=0
NewShipType.swayOffsetRandomX=0
NewShipType.swayOffsetRandomY=0
NewShipType.swayOffsetRandomZ=0
NewShipType.swayBobbingFactor=0
NewShipType.swayRotateFactor=0

...

addAbility(NewShipType,"MoveCommand",0,0);

data:leveldata/multiplayer/lib/camerautils.lua

AXIS_DISTANCE = 10000;

function Camera_Init()
    Volume_AddSphere("Volume_Map0", {0, 0, 0}, 0.01)
    Volume_AddSphere("Volume_MapX", {AXIS_DISTANCE, 0, 0}, 0.01)
    Volume_AddSphere("Volume_MapY", {0, AXIS_DISTANCE, 0}, 0.01)
    Volume_AddSphere("Volume_MapZ", {0, 0, AXIS_DISTANCE}, 0.01)
    
    SobGroup_Create("SobGroup_Map0")
    SobGroup_Create("SobGroup_MapX")
    SobGroup_Create("SobGroup_MapY")
    SobGroup_Create("SobGroup_MapZ")

    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_0", "SobGroup_Map0", "Volume_Map0")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_X", "SobGroup_MapX", "Volume_MapX")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Y", "SobGroup_MapY", "Volume_MapY")
    SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Z", "SobGroup_MapZ", "Volume_MapZ")
    
    SobGroup_SetInvulnerability("SobGroup_Map0", 1)
    SobGroup_SetInvulnerability("SobGroup_MapX", 1)
    SobGroup_SetInvulnerability("SobGroup_MapY", 1)
    SobGroup_SetInvulnerability("SobGroup_MapZ", 1)
    
    ATI_LoadTemplates("data:leveldata/multiplayer/lib/camera_ati.lua")
    
    Rule_AddInterval("UpdateCameraPos", 1)
end

function Camera_GetPosition()
    local a = Camera_GetDistanceToSobGroup("SobGroup_Map0")
    local b = Camera_GetDistanceToSobGroup("SobGroup_MapX")
    local c = Camera_GetDistanceToSobGroup("SobGroup_MapY")
    local d = Camera_GetDistanceToSobGroup("SobGroup_MapZ")
    
    local z = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - d*d) / (2*AXIS_DISTANCE);
    local y = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - c*c) / (2*AXIS_DISTANCE);
    local x = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - b*b) / (2*AXIS_DISTANCE);
    
    return { X = x, Y = y, Z = z};
end

function UpdateCameraPos()
    local pos = Camera_GetPosition();
    ATI_Clear()
    ATI_CreateParameters(1)
    ATI_AddString(0, format("campos: %.4f, %.4f, %.4f", pos["X"], pos["Y"], pos["Z"]))
    ATI_Display2D("Message", {0.02, 0.2, 0, 0}, 0)
end

data:leveldata/multiplayer/lib/camera_ati.lua:

SCAR_ATITemplates = {
    Message = {
        {
            stringParam = 0,
            text = {
                colour = {1, 1, 1, 1},
                dropshadow = 1,
                renderFlags = { "justifyLeft" },
                LODs = { 1, "SPSubtitleFont" }
            },
            placement2D = {
                factorX = -0.05,
                factorY = -1,
                minATIArea = 0,
                maxATIArea = 1,
                visibility = {}
            }
        }
    }
}

In data:scripts/rules/deathmatch/deathmatch.lua:

Add dofilepath("data:leveldata/multiplayer/lib/camerautils.lua") after other dofilepaths.
Add Camera_Init() before end of OnInit()

Run the game. See Camera Position displayed.

Also, ships tend to have a random “sway” attached to them, even when stationary, so this isn’t a 100% exact science… But it will get you 99.9% there, which in my book is plenty! :wink:

3 Likes

I’m sure a trigger could be made, yes. I’ll leave that to others.

Direction might be possible, but I doubt it. If it is, it would be really complex…

Edit: Actually, I lied. I just figured out how to do it.

Press key, save camera position.
Zoom In/Out
Press key again, save second camera position.
Calculate difference in positions and normalize vector, keeping in mind if you zoomed out or in.
(If you just zoom in (assuming you’re not already all the way zoomed in), and take the NewPos minus OldPos, you’ll have your direction vector).

Edit: Also regarding the ship sway: Ships don’t sway while the game is paused. Then again, this particular display of the camera position doesn’t update while the game is paused either. I suppose a keybind might be the way to go here, but I’m not sure if the keybind functions are called independently of the game clock ticking…

1 Like

Yup, already thinking key-bind when i got to this post.

In my use of the function I start with a map and then fly a scout around marking position where I want the fleet to be, value written to log (screen is nice touch but i can extract from log with script faster and write to LEVEL file with correct syntax automatically).

Another nice time saver, I can move through the map and mark points for a patrol path or camera path quickly as well. This saves a lot of time over all the guesswork of plotting XYZ manually and running game to see if objects are where I expect them to be.

Now time to make some missions! :slight_smile:

Thanks All!

Ah, you were faster than me, I was also looking to create a trilateration algorithm ^^
Well done :slight_smile:

Your idea for the direction is great too !

Edit :
I suppose the direction thing could be automated by activating sensor mode, taking one measure, deactivating sensor mode, taking another measure. As sensor mode is an “automatic” zoom out, you would have your two points this way. The function to do this should be MainUI_UserEvent(eSensorsManager)

The maths turned out to be a lot more simple than I was expecting…

The formulas I saw for 3D trilateration were pretty complicated at first. Your idea of putting volume and spawning ship directly on the x, y, and z axis helped to greatly simplify them I think.

I think that’s exactly what did it. I’ll admit it was unintentional though.

Sometimes coincidences give great results ^^

1 Like

Okay, here is what I have found practical to use. This, once set up, can allow me to script quickly the exact location I want the camera to be with very little trial and error. Trial and error in the old way (just modifying the X, Y, X coordinates) took hours trying to script a simple scene.

I added everything @radar3301 has provided above.

I then added some code myself (disclaimer, I am not a LUA developer)
In \HomeworldRM\B8MissionMOD\leveldata\campaign\rr_oemb\m28_Return_gehenna.lua

Add Function:

function cheat_i()
print(“Camera Position”)
local leaderPosition = SobGroup_GetCentrePosition(“HumanPlayerSOB”)
print(“leaderPosition = {”…leaderPosition[1]…", “…leaderPosition[2]…”, “…leaderPosition[3]…”},")
local pos = Camera_GetPosition();
print(pos[“X”], pos[“Y”], pos[“Z”])
end

And in the function: function Rule_Init()

UI_BindKeyEvent( IKEY, ‘cheat_i’ )

Summary

Basically, in making scenes, I use the AddCamera to control where the player is looking. AddCamera requires two sets of XYZ coordinates, first set being where the camera will look and the second set of the base point of the camera.
e.g.

addCamera(“camera1”, {0.000000, 0.000000, 0.000000}, {-252.000000, 1548.000000, 5791.000000})

In game, I select an object (ship, asteroid, etc). I move the screen to position how I want the player to see it. then I press “i”.

Using the code in my example:

local leaderPosition = SobGroup_GetCentrePosition(“HumanPlayerSOB”)
print(“leaderPosition = {”…leaderPosition[1]…", “…leaderPosition[2]…”, “…leaderPosition[3]…”},")

Returns this in the HwRM.log:

leaderPosition = {720.3656005859375, 828.7918090820313, 968.1722412109375},

Since in most scenes the player is going to look at something, I use the “something” to provide the coordinates of WHERE the player is looking.

Then using @radar3301 code, which is activated by me calling;

local pos = Camera_GetPosition();
print(pos[“X”], pos[“Y”], pos[“Z”])

Returns this in the HwRM.log:

-252.1182701686859

1543.764143411255

5792.635323712289

This is the BASE POINT.

I copy those two prints from the log into the addCamera in my m28_Return_gehenna.level file. And I now have a camera starting point that will start near exactly where I hit “i” in my game.

WISHLIST to make this even easier.
My first PRINT statement is a near perfect copy-paste from log to level file. I only have to trim the extra digits past 5. However @radar3301 function returned values printed are on 3 lines and I have to massage the data more to get into the AddCamera. I have been trying to do string manipulation in order to automatically scrape the log and write the values in the correct syntax for an easier copy-paste but, and with my limited LUA knowledge, I have not been successful. Print looks to be more a simple function to view results quickly. I can do this outside of LUA with C#, PowerShell, etc but that requires using another tool to do something that may be able to be inherent in this function on its own?

Basically it would be nice to turn this:

Camera Position
leaderPosition = {720.3656005859375, 828.7918090820313, 968.1722412109375},
-252.1182701686859

1543.764143411255

5792.635323712289

into this:

addCamera(“camera1”, {720.365600, 828.79180, 968.17224}, {-252.118270, 1543.764143, 5792.635323})

It is a small thing… but as a programmer I am always looking for ways to automate to make things easier. :slight_smile:

@BitVenom I am also curious, the camActions you showed us in this post:

Is it possible to use that current functionality to control camera movements for a Mission Map? Or is CamAction just a tool for making videos?

Currently in my attempts to make a mission map, when I want to make a “scene” for the player using the in game units I just use normal HW2 scripting reference to script in lua.

local pos = Camera_GetPosition()
print(pos["X"], pos["Y"], pos["Z"])

should be

local pos = Camera_GetPosition()
print("campos = {" .. pos.X .. ", " .. pos.Y .. ", " .. pos.Z .. "}")

to get it to print on one line.

Also, not sure if the format library function is available in GameRules, but if it is (duh, it is. It’s used in the ATI_AddString function in camerautils.lua [facepalm]), this will help you immensely:

local pos = Camera_GetPosition()
print(format("campos = {%0.5d, %0.5d, %0.5d}", pos.X, pos.Y, pos.Z))

(And yes, you can reference named table keys like that :slight_smile: ).


EDIT:

Even better:

local dst = SobGroup_GetCentrePosition("HumanPlayerSOB")
local src = Camera_GetPosition()
print("addCamera(\"camera1\", " .. format("{%f, %f, %f}, {%f, %f, %f}", dst[1], dst[2], dst[3], src.X, src.Y, src.Z) .. ")\n")

EDIT:
Fixed print code.

4 Likes

I didn’t know you could use format() with several variables in one call, that’s good to know !

That’s the whole point of format. :slight_smile:

I fixed the addCamera print line above, reposted here:

local dst = SobGroup_GetCentrePosition("HumanPlayerSOB")
local src = Camera_GetPosition()
print("addCamera(\"camera1\", " .. format("{%f, %f, %f}, {%f, %f, %f}", dst[1], dst[2], dst[3], src.X, src.Y, src.Z) .. ")\n")
1 Like

Ship sway actually doesn’t affect the calculations (even though ships do sway). I printed out the values of the positions of the probes used as “anchors”, and they don’t change. I’m now of the opinion that the camera sways slightly. I did a quick test using a KeyBind (yes, they work, even while paused), and without moving the camera, got several differing values (I think it’s an “elastic” camera). Again though: