I think I found heightmap data but would like feedback menu

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 15 of 31
  1. #1
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    I think I found heightmap data but would like feedback

    In Lioneye's Watch, I was able to get PoEHeightmap.zip from LocalData + 0x640 (Terrain) + 0x28. For reference Capture2.PNG is where the Waypoint is. The data is tiles of 23x23 bytes with a struct size of 0x38. The struck also has the Metadata link for the tdt file such as Metadata/Terrain/Beach/Shoreline/beach_oceanfloor_v01_01.tdt. Has anyone else looked into this?

    Edit: It looks like there is a rotation component I am missing.

    Pastbin kept deleting the post so I have attached it as a zip file.

    Here is the code I used to get it.

    Code:
    first = Memory.Read<IntPtr>(Address, 0x28);
    byte[,] heightmap = new byte[cols * 23, rows * 23];
    for (int i = 0; i < cols * rows; i++)
    {
        IntPtr address = Memory.Read<IntPtr>(first, new int[] { (0x38 * i) + 8, 0 });
        byte[] temp = Memory.Read(23 * 23, address);
        for (int j = 0; j < 23 * 23; j++)
        {
            int x = j / 23 + ((i % cols) * 23);
            int y = 22 - (j % 23) + ((i / cols) * 23);
            heightmap[x, y] = temp[j];
        }
    }
    Last edited by natemiddleman; 04-11-2021 at 05:42 AM.

    These ads disappear when you log in.

  2. #2
    pushedx's Avatar Contributor
    Reputation
    248
    Join Date
    Nov 2009
    Posts
    133
    Thanks G/R
    5/126
    Trade Feedback
    0 (0%)
    Mentioned
    12 Post(s)
    Tagged
    0 Thread(s)
    I think you're on the right track for the heightmap data, but it's raw data.

    The game has a function, I call it GetHeightAt, and it uses the raw heightmap data along with additional calculations to get the real terrain height based on what's around you. From the code you've posted, I don't think you could arrive at the correct heights with that alone. The best way to find the height function would be to track down what uses the vector and eventually you should find a function that's referenced a lot that returns the height (as I used to call that function directly to get the height, but only recently reversed it to emulate getting the height without any client function calls, it's a bit of work)

    The easy way to check though, is check whatever height you calculate for the player against the height found in the Render component I think it was. I don't have it updated for the current version of the game, but if you go to a hideout and find a place with 0 height, and then a place with a slightly different height, your height calc should be pretty close as the heights you find in the components won't always match due to animation interpolation or being relative from the center of the character rather than from the bottom. I forgot exactly what the oddity was, but I noticed it when I last did height stuff last year. Eventually I was able to correlate the differences and just checked what I calculated against what the function was returning to verify I had the math right.

  3. #3
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    a friend of mine (along with me) were doing this 2 days ago. wow, again, what a coincidence. In the 0x38 sized structure you found, height is @ offset 0x30. it's a byte, you basically take that, divide it by 2 for 3 times (i.e. shift left 3 times) and then multiply it by 7.81. I will share the full details when I understand more.


    also, I get this information by reading the GetHeightAt function. I will share the location of GetHeightAt function when i get some free time.
    TIP: it can be found by going into player's "Render" componen's Z-Axis data ( offset 0xE0 )and clicking CE "What writes to this address" .
    Last edited by zaafar; 04-15-2021 at 04:02 AM.

  4. #4
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    i don't understand the following 2 lines.

    ```
    IntPtr address = Memory.Read<IntPtr>(first, new int[] { (0x38 * i) + 8, 0 });
    byte[] temp = Memory.Read(23 * 23, address);
    ```

    are you trying to read every 2nd pointer of that 0x38 sized struct? Also, once you are inside that 2nd pointer, are you reading 23 * 23 bytes from that 2nd pointer?

    Also, following XY calculation is a bit confusing too.


    ```
    int x = j / 23 + ((i % cols) * 23);
    int y = 22 - (j % 23) + ((i / cols) * 23);
    ```





    Attaching my CE file (height_calc.txt) (change ext from .txt to .ct) which shows the functions involved in calculating height.
    I opened those functions in Ghidra to understand how map X, Y is converted to height.
    The Math/calculations over there are a bit different than what you are showing but then again, i haven't understood everything, it's WIP.
    Last edited by zaafar; 04-15-2021 at 06:49 AM.

  5. #5
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    1.png

    At Terrain + 0x28 is First, Last, and End. The struck size is 0x38 and there are 540 of them in this example 18 x 30 for Lioneye's Watch.

    2.png

    The data is at the start of each struct + 0x8.

    3.png

    The data is 529 (23 x 23) bytes long which makes up one square tile.

    I am pretty sure my formula is wrong.

    int x = j / 23 + ((i % cols) * 23);
    int y = 22 - (j % 23) + ((i / cols) * 23);

    I think it should be the following and then rotated.

    int x = j % 23 + ((i % cols) * 23);
    int y = j / 23 + ((i / cols) * 23);

    My issue is I don't know where the rotation is located and I don't know how to read assembly yet so I am a little stuck at the moment.
    Last edited by natemiddleman; 04-15-2021 at 06:07 PM.

  6. #6
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Calculating height doesn't have anything to do with rotation (As far as I understand) maybe I am forgetting something. I am a bit tired so i might change my comment on the weekend. Here's what I found after reading the code.


    POE Game Code that calculate Height from Grid Position X, Y
    Code:
    int FUN_140ba8350(longlong TerrainDataStartPtr,int *GridPos)
    
    {
      int iVar1;
      longlong SPECIFIC_TGT_DATA_START;
      int GridPosX;
      int GridPosY;
      
      GridPosX = *GridPos;
      if ((((-1 < GridPosX) && (GridPosY = GridPos[1], -1 < GridPosY)) &&
          (SBORROW4(GridPosX,*(int *)(TerrainDataStartPtr + 0x18) * 0x17) !=
           GridPosX + *(int *)(TerrainDataStartPtr + 0x18) * -0x17 < 0)) &&
         (SBORROW4(GridPosY,*(int *)(TerrainDataStartPtr + 0x20) * 0x17) !=
          GridPosY + *(int *)(TerrainDataStartPtr + 0x20) * -0x17 < 0)) {
        SPECIFIC_TGT_DATA_START =
             ((ulonglong)(uint)(GridPosY / 0x17) * *(longlong *)(TerrainDataStartPtr + 0x18) +
             (ulonglong)(uint)(GridPosX / 0x17)) * 0x38 + *(longlong *)(TerrainDataStartPtr + 0x28);
        if (*(longlong *)(SPECIFIC_TGT_DATA_START + 8) != 0) {
          GridPosX = GridPosX % 0x17;
          GridPosY = GridPosY % 0x17;
          iVar1 = FUN_140c438a0(*(longlong *)(SPECIFIC_TGT_DATA_START + 8),&GridPosX,
                                *(byte *)(SPECIFIC_TGT_DATA_START + 0x30) & 7);
          return iVar1 + ((int)*(short *)(SPECIFIC_TGT_DATA_START + 0x30) >> 3) *
                         *(int *)(TerrainDataStartPtr + 0x80);
        }
      }
      return 0;
    }


    my translation of that code with variable names that human understand.

    Code:
    TOTAL_COLUMNS_IN_A_ROW = READ(TerrainDataStartPtr + 0x18)
    
    TOTAL_ROWS = READ(TerrainDataStartPtr + 0x18 + 0x04) 
    
    INT_PTR TGT_DATA_LIST_START = READ(TerrainDataStartPtr + 0x28);
    
    int CURRENT_MAP_HEIGHT_MULTIPLIER = READ(TerrainDataStartPtr + 0x80)
    
    float CURR_TGT_DATA_ROW = (uint)(GridPosY / 23)
    float CURR_TGT_DATA_COL = (uint)(GridPosX / 23)
    
    
    
    uint index = (CURR_TGT_DATA_ROW * TOTAL_COLUMNS_IN_A_ROW) + CURR_TGT_DATA_COL
    
    
    bytes_to_skip = index * 0x38
    
    
    SPECIFIC_TGT_DATA_START = bytes_to_skip + TGT_DATA_LIST_START;
    
    SPECIFIC_TGT_DATA_2nd_PTR = SPECIFIC_TGT_DATA_START + 0x08;
    
    if (SPECIFIC_TGT_DATA_2nd_PTR == 0x00)
    {
         return 0;
    }
    else
    {
            int GridPosX_Remaining = GridPosX % 23
            int GridPosY_Remaining = GridPosY % 23
            height_of_that_specific_part_of_x23_x23_tile = FUN_140c438a0(some args.....)
    		height_of_THAT_SPECIFIC_TGT_DATA = READ(SPECIFIC_TGT_DATA_START + 0x30) >> 3
    		return height_of_that_specific_part_of_x23_x23_tile + (height_of_THAT_SPECIFIC_TGT_DATA * CURRENT_MAP_HEIGHT_MULTIPLIER)
    }


    NOTE: FUN_140c438a0 is still not translated yet ( by me ). It's WIP.
    NOTE 2: What you are doing, I think that happens in FUN_140c438a0. (i might be wrong since it's not translated yet.
    NOTE 2: in my translation I ignored the first whole if condition (as shown below), basically i assumed it's always gonna be true unless data is corrupted. This assumption can be wrong, I will fix this assumption once I am done with FUN_140c438a0 function.

    Code:
      if ((((-1 < GridPosX) && (GridPosY = GridPos[1], -1 < GridPosY)) &&
          (SBORROW4(GridPosX,*(int *)(TerrainDataStartPtr + 0x18) * 0x17) !=
           GridPosX + *(int *)(TerrainDataStartPtr + 0x18) * -0x17 < 0)) &&
         (SBORROW4(GridPosY,*(int *)(TerrainDataStartPtr + 0x20) * 0x17) !=
          GridPosY + *(int *)(TerrainDataStartPtr + 0x20) * -0x17 < 0))
    Last edited by zaafar; 04-15-2021 at 03:50 PM.

  7. #7
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by natemiddleman View Post
    1.png

    At Terrain + 0x28 is First, Last, and End. The struck size is 0x38 and there are 540 of them in this example 18 x 30 for Lioneye's Watch.

    2.png

    The data is at the start of each struct + 0x8.

    3.png

    The data is 529 (23 x 23 bytes long) which makes up one square tile.

    I am pretty my formula is wrong.

    int x = j / 23 + ((i % cols) * 23);
    int y = 22 - (j % 23) + ((i / cols) * 23);

    I think it should be the following and then rotated.

    int x = j % 23 + ((i % cols) * 23);
    int y = j / 23 + ((i / cols) * 23);

    My issue is I don't know where the rotation is located and I don't know how to read assembly yet so I am a little stuck at the moment.


    thanks for explaining this. this makes everything 10000 times more sense. now i can easily solve the rest of the puzzle.

    with respect to your formula being wrong and not rotated. I will answer that once i have solved the whole puzzle.



    just an FYI: there is a lot more data in those 0x38 size structure than what we are currently reading. There is a reason that data structure is of size 0x38. not 0x08 or 0x10 or 0x24.
    I have a feeling it will be in our benefit to reverse all of that 0x38 sized data (and data inside it's pointers) than stopping at "height".



    btw why are you calculating height? I am doing it for a maphack i am creating, what's your use-case (care to share)??
    Last edited by zaafar; 04-15-2021 at 04:05 PM.

  8. #8
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    I have a basic path-finding algorithm working with A* but calculating the correct point on the screen I need to click requires a correct height value. Right now if I move only a small amount and use the players render height it works ok but not well.

    4.png Edit: Fixed?

    This is an example of the last 8 bytes of each struct. Everything else is just zeros. Initially I thought the first byte was the rotation but the range is between 0-7 not 0-3.

    I just had an idea while writing this. What if it is a combination of rotating and mirroring? 0-3 is rotation and 4-7 is rotation + mirror maybe? I will need to test.
    Last edited by natemiddleman; 04-15-2021 at 05:54 PM.

  9. #9
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    The attachment is invalid.

  10. #10
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by natemiddleman View Post
    I have a basic path-finding algorithm working with A* but calculating the correct point on the screen I need to click requires a correct height value. Right now if I move only a small amount and use the players render height it works ok but not well.

    Attachment 77147

    This is an example of the last 8 bytes of each struct. Everything else is just zeros. Initially I thought the first byte was the rotation but the range is between 0-7 not 0-3.

    I just had an idea while writing this. What if it is a combination of rotating and mirroring? 0-3 is rotation and 4-7 is rotation + mirror maybe? I will need to test.
    Oh! You want to rotate the map because clicking on the computer monitor screen is different from clicking on gridPosXY? You want to take gridPosXY and convert it into a point on the monitor screen?

    If that’s the case then it’s already done in exileApi. Read exileapi code.


    Edit: and you want to do that to a future point, where your player will be, not where your player currently is. Hmm, I understand now. It’s not done in exileapi.


    Edit 2: when you say rotation, I am assuming you mean the 45* map rotation.


    Edit 3: are you 100% sure that the final height of the grid position X and Y have to be rotated 45 degree. It still doesn’t make any sense.
    Last edited by zaafar; 04-15-2021 at 05:01 PM.

  11. #11
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    No, the 23x23 array needs to be rotated 90 degrees. I am now sure struct + 0x30 is the rotation counterclockwise if it is between 0-3. I think 4-7 is flip then rotate. I just need to determine what axis it is flipping on first.
    Last edited by natemiddleman; 04-15-2021 at 05:45 PM.

  12. #12
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    So it is rotate counterclockwise then flip across the y axis. Here is the output with the code below. Now I just need to figure out how to translate it to world coordinates.

    PoEHeightmap.zip

    Code:
                for (int i = 0; i < cols * rows; i++)
                {
                    IntPtr address = Memory.Read<IntPtr>(first, new int[] { (i * 0x38) + 0x8, 0 });
                    byte[] temp = Memory.Read(23 * 23, address);
                    
                    // To Array
                    byte[,] array = new byte[23, 23];
                    for (int j = 0; j < 23 * 23; j++)
                    {
                        int x = j % 23;
                        int y = j / 23;
                        array[x, y] = temp[j];
                    }
    
                    byte rotation = Memory.Read<byte>(first, new int[] { (i * 0x38) + 0x30 });
    
                    // Rotate
                    for (int j = 0; j < (rotation < 0x4 ? rotation : rotation - 0x4); j++)
                    {
                        byte[,] rotate = new byte[23, 23];
    
                        for (int x = 0; x < 23; ++x)
                        {
                            for (int y = 0; y < 23; ++y)
                            {
                                rotate[x, y] = array[y, 23 - x - 1];
                            }
                        }
                        array = rotate;
                    }
    
                    // Flip
                    if (rotation >= 0x4)
                    {
                        byte[,] flip = new byte[23, 23];
                        for (int x = 0; x < 23; x++)
                        {
                            for (int y = 0; y < 23; y++)
                            {
                                flip[x, y] = array[23 - x - 1, y];
                            }
                        }
                        array = flip;
                    }
    
                    // To Heightmap
                    for (int j = 0; j < 23 * 23; j++)
                    {
                        int x = j % 23 + ((i % cols) * 23);
                        int y = j / 23 + ((i / cols) * 23);
                        heightmap[x, y] = array[j % 23, j / 23];
                    }
                }
    Last edited by natemiddleman; 04-15-2021 at 06:04 PM.

  13. #13
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by natemiddleman View Post
    No, the 23x23 array needs to be rotated 90 degrees. I am now sure struct + 0x30 is the rotation counterclockwise if it is between 0-3. I think 4-7 is flip then rotate. I just need to determine what axis it is flipping on first.
    Oooo, now I understand and it make sense. Hmm why I did not think about that before. Lol Thanks!


    I will validate your math by reading the Func_xxxx function and see how is game doing the rotation.
    Last edited by zaafar; 04-15-2021 at 07:37 PM.

  14. #14
    zaafar's Avatar ★ Elder ★
    Reputation
    1183
    Join Date
    Jun 2015
    Posts
    1,810
    Thanks G/R
    242/1040
    Trade Feedback
    0 (0%)
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    the way game is working is that it's not cloning & rotating the array 4 times or flipping the array 1 time (that's slow and waste a lot of memory). It's basically calculating the X, Y after rotation ( where x, y can be between 0 - 0x23) and using that to get the height value. Also, there is some static matrix/bytes involve to help the function rotate X,Y.

    Code:
    int FUN_140c438a0(longlong *F23X23_DATA_START,int *Remaining_Grid_XY,byte Unknown000)
    
    {
      longlong lVar1;
      uint uVar2;
      int local_18 [6];
      
      if (*F23X23_DATA_START == F23X23_DATA_START[1]) {
        return 0;
      }
      local_18[1] = *Remaining_Grid_XY;
      lVar1 = (ulonglong)(byte)(&DAT_141a44e48)[Unknown000] * 3;
      uVar2 = 0;
      local_18[0] = 0x16 - *Remaining_Grid_XY;
      local_18[3] = Remaining_Grid_XY[1];
      local_18[2] = 0x16 - Remaining_Grid_XY[1];
      if ((&DAT_141a44d60)[lVar1] == 0) {
        uVar2 = 2;
      }
      return (int)*(char *)((longlong)
                            local_18[(ulonglong)(byte)(&DAT_141a44d61)[lVar1] +
                                     (ulonglong)(byte)(&DAT_141a44d60)[lVar1] * 2] +
                           *F23X23_DATA_START +
                           (longlong)
                           local_18[(ulonglong)uVar2 + (ulonglong)(byte)(&DAT_141a44d62)[lVar1]] * 0x17)
      ;
    }


    what i really don't understand is that the same data that it is using for the purpose of rotating ( i.e the byte at 0x30 ). it is also using that byte to multiply and add to the final output of height.


    POE code
    Code:
    RotationFlippingSelector -----> *(byte *)(SPECIFIC_TGT_DATA_START + 0x30) & 7
    
    
    Multiplier ------>  ((int)*(short *)(SPECIFIC_TGT_DATA_START + 0x30) >> 3)       *            *(int *)(TerrainDataStartPtr + 0x80)
    my translation
    Code:
    byte foobar = M.READ(SPECIFIC_TGT_DATA_START + 0x30)
    int ROTATION_AND_STUFF_INFO = foobar & 7; // least significant bit
    
    short foofoo = M.READ(SPECIFIC_TGT_DATA_START + 0x30) // short = 2 bytes
    int height_of_THAT_SPECIFIC_TGT_DATA = foofoo >> 3


    actually while writing this, i had an idea, it might be little endian and big endian thing...it might not be using the same byte. hmm let me test it.


    EDIT: just confirmed. so *3 lest significant bit* of 0x30 is used as a selector to rotation/flipping. 0x30 - 0x31 excluding the 3 least significant bit is used as multiplier.
    so if we read a "short" @ 0x30. we can do "value & 7" to get rotation_and_flipping_selector and "value >> 3" to get the multiplier.
    Last edited by zaafar; 04-15-2021 at 09:05 PM.

  15. #15
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    This is driving me nuts. I have two different tiles with 0x30 % 7 = 3 that need to be rotated differently. Reading 0x30 as a byte and short was the same. Maybe it needs to be read as an int or long but the server shut down before I had a chance to test it. I probably won't do much work for at least a week after league start.

Page 1 of 3 123 LastLast

Similar Threads

  1. Found entity data, but cannot locate some key pieces
    By HoboDoug in forum Final Fantasy XIV
    Replies: 3
    Last Post: 03-01-2016, 08:47 PM
  2. I think I found something really WTF.
    By Lyeden in forum Community Chat
    Replies: 0
    Last Post: 10-26-2015, 05:44 AM
  3. I would like to trade but I am not sure about this.
    By zFortune in forum World of Warcraft General
    Replies: 3
    Last Post: 08-06-2009, 04:05 PM
  4. [Rate] I think i found my style :D
    By Maine in forum Art & Graphic Design
    Replies: 4
    Last Post: 07-27-2008, 06:08 AM
All times are GMT -5. The time now is 01:47 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2021 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2021 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search