Download
(13 Kb)
Download
Updated: 03/11/20 01:27 PM
Compatibility:
Harrowstorm (5.3.5)
Elsweyr (5.0.5)
Updated:03/11/20 01:27 PM
Created:04/13/17 12:39 PM
Monthly downloads:3,093
Total downloads:312,554
Favorites:267
MD5:
Lib3D-v3  Popular! (More than 5000 hits)
Version: 3.12
by: Shinni [More]
This library offers utility functions for the 3D control API, such as
  • converting between 3D API coordinates and 2D map coordinates
  • converting 2D map distances and "real" distances in meters
  • functions to obtain the player's 3D position


How To Use:
Add Lib3D as DependsOn library, i.e. in your addon's manifest you should write
Code:
## DependsOn: Lib3D
And either install this library directly, or include it in your addon.
Note that this library depends on LibGPS, so you need to install/include LibGPS as well.

Coordinate Systems

There exist 4 different coordinate systems:
Local:
The coordinates with respect to the currently open 2D map (see LibGPS)
(0,0) is the top left corner and (1,1) is the bottom right corner of the current 2D map.
Global:
The coordinates with respect to the Tamriel 2D map (see LibGPS)
(0,0) is the top left corner and (1,1) is the bottom right corner of the Tamriel 2D map.
Note that these coords can be negative for zones such as Coldharbor.
GUI (also called 3DRenderSpace):
The coordinates used by ZOS' 3D controls API. 1 unit is 1 meter, but the origin of the coordinate system changes over time.
E.g. when moving 1km away from the GUI origin, the current player position becomes the new origin.
(0,y,0) is a point at the current world origin and at height y.
(1,y,0) is 1 meter to the east, while (0,y,1) is 1 meter to the south.
When the origin moves, ZOS' 3D control API will automatically move all 3D-TopLevelControls as well, so the controls are still visible at the correct locations. Just something I want to point out, because this means the result of TopLevelControl:Get3DRenderSpaceOrigin() can change without you calling TopLevelControl:Set3DRenderSpaceOrigin(x,y,z) !
World (ZOS):
The 3D coordinates used to measure the entire zone. The origin the the north western corner of the zone's 3D map file and the units are in centimeters. Note that the origin of World and GUI system differ! So the same point can have different coordinates in the World and GUI system.
You can convert between the two systems using ZOS' WorldPositionToGuiRender3DPosition/GuiRender3DPositionToWorldPosition API functions.
This world coordinate system is used by ZOS' housing api and by the SetPlayerWaypointByWorldLocation API function.

World (Lib3D):
Same as World (ZOS) but the units are in meters. All functions of library will use this system. So for instance worldX, worldZ = lib3D:GlobalToWorld(x,y) will convert coordinates from libGPS' global coordinate system to Lib3D's world coordinates.


If you want to save a position in a SavedVars table, I recommend using global coordinates + height (world y coordinate). Historically speaking, global coordinates are the most resilient against API changes.

Converting between coordinate systems

Local and Global coordinates can be converted using LibGPS ( libGPS:LocalToGlobal(x,y) and libGPS:GlobalToLocal(x,y) ).
GUI and World (ZOS) coordinates can be converted using ZOS' API functions WorldPositionToGuiRender3DPosition(x,y,z) and GuiRender3DPositionToWorldPosition(x,y,z).
World (Lib3D) coordinates can be converted to local and global coordinates via
worldX, worldZ = lib3D:GlobalToWorld(x,y)
globalX, globalY = lib3D:WorldToGlobal(worldX,worldY,worldZ)
worldX, worldZ = lib3D:LocalToWorld(localX,localY)
localX, localY = lib3D:WorldToLocal(worldX,worldY,worldZ)
.
World (Lib3D) can be converted to World (ZOS) by multiplying with 100 (converting from meters to centimeters).

Measureing distances

Lib3D offers the following functions to compute the distance in meters between 2D map points (local or global):

lib3D:LocalDistanceInMeters(x1, y1, x2, y2)
lib3D:GlobalDistanceInMeters(x1, y1, x2, y2)


You can also get squared distances via
lib3D:LocalDistance2InMeters(x1, y1, x2, y2)
lib3D:GlobalDistance2InMeters(x1, y1, x2, y2)


Player and camera positions

The player position in the 3D world can easily be computed via worldX, worldZ = lib3D:LocalToWorld(GetMapPlayerPosition("player"))
but this will only return the position in the 2D plane, i.e. you do not get any information about the player height.
The following functions are usefull for the case, that you also require the player's height.

worldX, worldY, worldZ = lib3D:GetCameraRenderSpacePosition()
returns the camera position in GUI coordinates

worldX, worldY, worldZ = lib3D:GetFirstPersonRenderSpacePosition()
return the first person camera position in GUI coordinates. This is effectively the player's 3D position.
If the camera is currently in 3rd person, this function needs to switch the camera to first person for 1 frame.
This function's result can be wrong when using addons that manipulate the first/third person camera such as ImmersiveHorseRiding, Immersive Mount Camera or First Person Werewolf.
If you want the player's position, I generally recommend to use the next function instead:

worldX, worldY, worldZ = lib3D:ComputePlayerRenderSpacePosition()
computes the player's position in GUI coordinates. This is generally the function you want to use to get the player's position, as it does not have to switch between 3rd and 1st person camera.
However, the result is not entirely accurate and can be off by a few centimeters.

Callbacks
lib3D:RegisterWorldChangeCallback(identifier, callback)
This function expects an identifier in addition to the callback. The identifier can be used to unregister the callback again.
The registered callback will be fired during EVENT_PLAYER_ACTIVATED, after lib3D has finished initialization for the current zone.
The callbacks arguments are identifier, zoneIndex, isValidZone and isNewZone.
The identifier is the identifier you provided at the registration.
zoneIndex is the current zone's index (here zone means a 3D map/area). It's the same as GetUnitZoneIndex("player").
isValidZone is a boolean and true, if the librarires functions can be used for the current zone.
isNewZone is true, when a new zone is entered, e.g. when the player teleported to a different zone or entered a delve etc. The value can be false when the player teleports from one wayshrine to another in the same area, or when they get a random loading screen.
Example:
Lua Code:
  1. lib3D:RegisterWorldChangeCallback("MyAddonName", function(identifier, zoneIndex, isValidZone, isNewZone)
  2.     if isNewZone then d("a new 3D world/zone was entered") end
  3. end)

lib3D:UnregisterWorldChangeCallback(identifier)
Unregisters the callback with the given identifier

lib3D:IsValidZone()
Returns true, if the libary finished initialization (happens during the first EVENT_PLAYER_ACTIVATED).
I.e. before the initialization, the conversion between the coordinate systems does not work.
You can (and should) register your callbacks and controls before the library initialized.

Example
The lib contains an example. Open the Lib3D.txt and uncomment the lines
Code:
example/example.xml
example/example.lua
This will add a little mage light floating around the player.
v3.12
- library dependencies are now accessed directly instead of using libstub

v3.11
- updated manifest

v3.10
- the Is* functions will now return proper booleans instead of truthy/falsy values

v3.9
- fixed typo that caused a nil error

v3.8
- added zoneMeasurement objects. You can query them via lib:GetZoneMeasurementForZoneId(zoneId) and then use zoneMeasurement:LocalToWorld etc.
This is just to help addons cache results, the old functions are still available.
- when the player enters a loading screen, the library will reset.
This way the conversion functions will not return wrong result if your addon does something after the loading screen, before the library measured the zone.

v3.4
- fixed WorldToGlobal conversion

v3.3
- fixed a bug where the coordinate conversion would not work when leaving and re-visiting a zone.

v3.2
- updated to standalone version.
Archived Files (7)
File Name
Version
Size
Uploader
Date
3.10
13kB
Shinni
05/29/19 12:56 PM
39
13kB
Shinni
05/26/19 02:36 PM
38
13kB
Shinni
05/26/19 09:46 AM
34
19kB
Shinni
02/27/19 10:11 AM
33
18kB
Shinni
10/26/18 06:26 AM
32
18kB
Shinni
10/24/18 01:27 PM
14
31kB
04/13/17 12:39 PM


Post A Reply Comment Options
Unread 01/17/21, 08:34 AM  
JLE

Forum posts: 0
File comments: 42
Uploads: 0
I was wondering that too. Any chance of an updated version that declares compatibility with Greymoor and Markarth?
Report comment to moderator  
Reply With Quote
Unread 06/25/20, 09:08 PM  
Shadowfen
AddOn Author - Click to view AddOns

Forum posts: 83
File comments: 750
Uploads: 15
Wild version numbers?

The version of Lib3D that is included in HarvestMap is 3.13, whereas the version that Minion says is the latest standalone is 3.12.
Report comment to moderator  
Reply With Quote
Unread 06/25/20, 09:04 PM  
Shadowfen
AddOn Author - Click to view AddOns

Forum posts: 83
File comments: 750
Uploads: 15
Nil error from library

I'm posting this error here and in Merlin's Rez Helper where it was called from.
I was running around Imperial City (all districts) and the sewers tonight, so I don't know which of the two the error occurred in.

Code:
user:/AddOns/HarvestMap/Libs/Lib3D/Lib3D.lua:187: attempt to index a nil value
stack traceback:
user:/AddOns/HarvestMap/Libs/Lib3D/Lib3D.lua:187: in function 'lib:GetCurrentWorldOriginAsGlobal'
<Locals> self = [table:1]{DEFAULT_GLOBAL_TO_WORLD_FACTOR = 25000} </Locals>
user:/AddOns/MerlinsRezHelper/merlinsRezHelper.lua:173: in function 'CalcLightPos'
<Locals> x = 0.34204614162445, y = 0.59414047002792 </Locals>
user:/AddOns/MerlinsRezHelper/merlinsRezHelper.lua:165: in function 'ShowLight'
user:/AddOns/MerlinsRezHelper/merlinsRezHelper.lua:125: in function 'UpdateReticle'
user:/AddOns/MerlinsRezHelper/merlinsRezHelper.lua:233: in function 'merlinsRezHelperUpdate'
<Locals> h = -1.2930310408222 </Locals>
merlinsRezHelperFrameRoot_Update:3: in function '(main chunk)'
<Locals> self = ud, time = 3599.3559570313 </Locals>
Report comment to moderator  
Reply With Quote
Unread 06/05/20, 01:41 AM  
Micke2nd
AddOn Author - Click to view AddOns

Forum posts: 43
File comments: 76
Uploads: 1
Exclamation Re: height

Originally Posted by Sordrak
I have an issue regarding the height. In the example you're using the camera's height, which is constantly changing when the player is moving around. So using that height isn't most likely a good idea.

Yet, i'm unable to find any information about the height of items. If we talk about the example on the addon's main page, the player is being used as location. is there a way to use the heading ( GetMapPlayerPosition(string unitTag) Returns: number normalizedX, number normalizedZ, number heading ) as some measurement of the current location?

I'm not really getting how all those map pins, which are set dynamically are capable of getting the right height while the camera height is obviously the wrong height to use.
I can confirm that.
Report comment to moderator  
Reply With Quote
Unread 10/30/18, 07:12 PM  
Marify
 
Marify's Avatar
AddOn Author - Click to view AddOns

Forum posts: 27
File comments: 155
Uploads: 10
nil will be returned

Hi!
Calling WorldToGlobal (x, z) returns x and nil.

-------------------------------
function lib:WorldToGlobal(x, z)
x = x * self.currentWorldToGlobalFactor
z = z * self.currentWorldToGlobalFactor
x = x + self.currentOriginGlobalX
z = z + self.currentOriginGlobalY
return x, y
end
--------------------------------
Report comment to moderator  
Reply With Quote
Unread 12/27/17, 07:45 AM  
Sordrak
 
Sordrak's Avatar
AddOn Author - Click to view AddOns

Forum posts: 52
File comments: 355
Uploads: 4
height

Hi

I'm a bit confused and might need some help here.

I have an issue regarding the height. In the example you're using the camera's height, which is constantly changing when the player is moving around. So using that height isn't most likely a good idea.

Yet, i'm unable to find any information about the height of items. If we talk about the example on the addon's main page, the player is being used as location. is there a way to use the heading ( GetMapPlayerPosition(string unitTag) Returns: number normalizedX, number normalizedZ, number heading ) as some measurement of the current location?

I'm not really getting how all those map pins, which are set dynamically are capable of getting the right height while the camera height is obviously the wrong height to use.
Report comment to moderator  
Reply With Quote
Unread 04/15/17, 10:03 AM  
Shinni
AddOn Author - Click to view AddOns

Forum posts: 167
File comments: 550
Uploads: 22
Great news. Thanks a lot!
Report comment to moderator  
Reply With Quote
Unread 04/14/17, 01:23 PM  
ZOS_ChipHilseberg
ZOS Staff!

Forum posts: 551
File comments: 7
Uploads: 0
The first change I'll be making is to automatically update the origin point on any toplevel gui render space that is using the WORLD system when the player moves more than 1KM from the origin. The second change will be adding GuiRender3DPositionToWorldPosition and WorldPositionToGuiRender3DPosition. And the third will probably be to never use the integer world coordinates ever again in Lua.
Last edited by ZOS_ChipHilseberg : 04/14/17 at 02:30 PM.
Report comment to moderator  
Reply With Quote
Unread 04/13/17, 05:17 PM  
Shinni
AddOn Author - Click to view AddOns

Forum posts: 167
File comments: 550
Uploads: 22
Originally Posted by ZOS_ChipHilseberg
If it would be useful to you we could expose a function that converts from what the GUI calls world coordinates to what the game client would call world coordinates (the integral centimeter values that you see in the housing APIs).
Yes, that would make many measurement easier and more accurate. If possible, I would also like to request an event which is fired whenever the origin of the GUI world coordinate system changes.

If the player moves 1km away from the origin of the GUI world coordinate system, then the origin is shifted to the current player position. This results in all existing 3D textures to be shifted by 1km.
Currently the library has to check every single frame, if the origin of the coordinate system changed. If a change occured, the library updates the position of existing controls to compensate for the shift.
An event would make this a lot easier, performant and accurate.
Report comment to moderator  
Reply With Quote
Unread 04/13/17, 01:14 PM  
ZOS_ChipHilseberg
ZOS Staff!

Forum posts: 551
File comments: 7
Uploads: 0
If it would be useful to you we could expose a function that converts from what the GUI calls world coordinates to what the game client would call world coordinates (the integral centimeter values that you see in the housing APIs).
Report comment to moderator  
Reply With Quote
Post A Reply



Category Jump: