Move D3D9 terrain flattening to Orbiter core

Kyle_E

Begining Double Bassist
Joined
Oct 24, 2016
Messages
63
Reaction score
24
Points
23
This feature is incredibly useful, but I would think it would be better managed in the core; especially as moving to x64 breaks the landing detection. This would also allow addons that rely on this feature to work with any graphics plugin.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
I'm not sure if this is the best way to implement it. Most of the heavy lifting is done on the OVP client side, because as it is now, the tile loading for all layers need to be done there, anyway. IMHO, a first step should be to implement a FilterElevation-callback in Orbiter's terrain-collision code, so the OVP client has the chance to manipulate the data loaded there the same way it does for visuals.

A second step could be to either completely out-source the tile-loading to the OVP client (because it is doing that job already), or reclaim tile-loading authority in the core again. I think the former is better, because that way things like super-sampling of tiles is in the command of the OVP client, making dissonance between visual and collision engine a thing of the past.
 

Kyle_E

Begining Double Bassist
Joined
Oct 24, 2016
Messages
63
Reaction score
24
Points
23
Yeah that makes more sense I guess.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
I think the former is better, because that way things like super-sampling of tiles is in the command of the OVP client, making dissonance between visual and collision engine a thing of the past.
Also, potential procedural fill-ins for non-existing or microterrain could be handled by the client as well, which has the potential advantage of leveraging the power of the GPU for such tasks.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
I've implemented the first step in my proposal. A PR has been sent to Martin, containing the necessary elevation filter callback: https://github.com/mschweiger/orbiter/pull/24 . The ZIP archive has been updated with a working x64 Orbiter+D3D9Client that respects the flattening configuration: https://snoopie.at/face/beta/x64-Debug.zip .
The changes to the D3D9Client (with ported flattening code) are as follows (EDIT: added patch as attachment below, because forum editor seems to mess it up):
Diff:
# HG changeset patch
# User face <[email protected]>
# Date 1627829270 -7200
#      Sun Aug 01 16:47:50 2021 +0200
# Node ID e6873b5d96eecda521cd327f92ce289e25a82567
# Parent  d2ac1f83f3961a25ad0ff4c2ec05b5c516955c0a
Flattening: exchanging hooking with API usage

diff --git a/Orbitersdk/D3D9Client/D3D9Client.cpp b/Orbitersdk/D3D9Client/D3D9Client.cpp
--- a/Orbitersdk/D3D9Client/D3D9Client.cpp
+++ b/Orbitersdk/D3D9Client/D3D9Client.cpp
@@ -231,9 +231,6 @@
     }
 };
 
-DWORD g_Hook;
-void *g_HookPoint = (void *)0x4185fc;
-void *g_TrampolineReturn = (void *)0x418604;
 std::unordered_map<OBJHANDLE, std::unordered_map<int, std::unordered_map<int, std::unordered_map<int, std::vector<FlatShape*>>>>> g_ElevationFlatteningShapes;
 std::unordered_map<OBJHANDLE, std::vector<FlatShape*>> g_ShapeStore;
 std::unordered_map<OBJHANDLE, bool> g_ShapesLoaded;
@@ -385,24 +382,8 @@
     g_ShapesLoaded[hPlanet] = true;
 }
 
-//The following array is:
-//_asm
-//{
-//    fld  qword [ebp+0x8]  // load floating point (lat or lng)
-//    fld  qword [ebp+0x10] // load floating point (lng or lat)
-//    fxch st1              // swap FPU storage
-//}
-byte g_Original[8] = { 0xdd, 0x45, 0x08, 0xdd, 0x45, 0x10, 0xd9, 0xc9 };
-
-//The following array is:
-//_asm
-//{
-//    jmp dword ptr [HOOKFUNCTION]; //jump to dynamically detected address
-//}
-byte g_Code[8] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
 template <typename Type>
-bool FilterElevation(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, Type *elev)
+bool FilterElevation(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, Type *elev)
 {
     if (!elev) return false;
     if (g_ShapesLoaded.find(hPlanet) == g_ShapesLoaded.end()) ProcessPlanetFlats(hPlanet);
@@ -436,7 +417,7 @@
             if (dist <= 1.0)
             {
                 // Do the math in INT16 basis even for "float" output
-                INT16 elevation = INT16(shape->Height);
+                INT16 elevation = INT16((double)shape->Height / elev_res);
                 if (shape->Falloff > 0)
                 {
                     for (auto pot = 0; pot < shape->Type; pot++)
@@ -446,7 +427,7 @@
                     if (dist >= 1 - shape->Falloff)
                     {
                         double alpha = (dist - 1 + shape->Falloff) / shape->Falloff;
-                        elevation = INT16(elev[i] * alpha + shape->Height * (1 - alpha));
+                        elevation = INT16(elev[i] * alpha + (double)shape->Height / elev_res * (1 - alpha));
                     }
                 }
                 // Convert to float or keep as an INT16 depending on case
@@ -458,13 +439,15 @@
 }
 
 
-void FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, INT16 *elev)
+bool FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, INT16 *elev)
 {
-    if (!Config->bFlatsEnabled) return;
+    if (!Config->bFlatsEnabled) return false;
     char name[64];
-    oapiGetObjectName(hPlanet, name, 64);
-    if (FilterElevation<INT16>(hPlanet, lvl, ilat, ilng, elev))
-        LogClr("Coral", "FilterElevation[Physics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);
+    oapiGetObjectName(hPlanet, name, 64);

+    auto result = FilterElevation<INT16>(hPlanet, lvl, ilat, ilng, elev_res, elev);
+    if (result)
+        LogClr("Coral", "FilterElevation[Physics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);

+    return result;
 }
 
 
@@ -473,69 +456,10 @@
     if (!Config->bFlatsEnabled) return;
     char name[64];
     oapiGetObjectName(hPlanet, name, 64);
-    if (FilterElevation<float>(hPlanet, lvl, ilat, ilng, elev))
+    if (FilterElevation<float>(hPlanet, lvl, ilat, ilng, 1.0, elev))
         LogClr("Coral", "FilterElevation[Graphics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);
 }
 
-void Trampoline()
-{
-    //__asm
-    //{
-    //    pop ebp                        // that's necessary because the compiler saved the pointer to the stack   
-    //    mov eax, dword ptr[edi]        // elev
-    //    mov ecx, dword ptr[esp + 0x54] // ilng
-    //    mov edx, dword ptr[esp + 0x2c] // ilat
-    //    push eax                       // arg4 = elev
-    //    mov eax, dword ptr[esp + 0x40] // internal GBody pointer
-    //    mov eax, dword ptr[eax]        // OBJHANDLE
-    //    push ecx                       // arg3 = ilng
-    //    push edx                       // arg2 = ilat
-    //    push esi                       // arg1 = lvl   
-    //    push eax                       // arg0 = OBJHANDLE
-    //    call FilterElevationPhysics       // filter function call
-    //    add esp, 14h                   // 5 arguments = 5*2 bytes = 20dec = 14h
-    //    fld qword ptr[ebp + 0x8]       // start of overwritten bytes in original position
-    //    fld qword ptr[ebp + 0x10]
-    //    fxch
-    //    jmp g_TrampolineReturn         // return from trampoline to original function
-    //}
-}
-
-int WriteCode(void *address, void *code, DWORD len)
-{
-    //Get process information
-    HANDLE hSelf = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, ::GetCurrentProcessId());
-    if (hSelf == NULL) return -1;
-    MEMORY_BASIC_INFORMATION mbi;
-
-    //Open up page of linked address
-    if (VirtualQueryEx(hSelf, (LPVOID)address, &mbi, sizeof(mbi)) != sizeof(mbi)) return -2;
-    PVOID pvRgnBaseAddress = mbi.BaseAddress;
-    DWORD dwOldProtect1, dwOldProtect2, dwFake;
-    if (!::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect1)) return -3;
-    BOOL bStridePage = FALSE;
-    LPBYTE lpByte = (LPBYTE)pvRgnBaseAddress;
-    lpByte += 4096;
-    if ((DWORD)lpByte < (DWORD)address + 4) bStridePage = TRUE;
-    PVOID pvRgnBaseAddress2 = (LPVOID)lpByte;
-    if (bStridePage)
-        if (!::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect2))
-        {
-            ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake);
-            return -4;
-        }
-
-    //Write code
-    memcpy(address, code, len);
-
-    //Lock again
-    ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake);
-    if (bStridePage) ::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, dwOldProtect2, &dwFake);
-
-    return 0;
-}
-
-
 
 
 
@@ -588,25 +512,7 @@
     LabelPos      (0)
 
 {
-    // =====================================================================
-    // Face's terrain flattening
-    // Hook up collision loader in core Orbiter
-    // Hooks are placed recardless of "Enable Terrain Flattening", simplier that way
-    // =====================================================================
-    union
-    {
-        void *pointer;
-        byte bytes[4];
-        DWORD value;
-    } p;
-
-    g_Hook = (DWORD)(void *)Trampoline;
-    p.pointer = (void *)&g_Hook;
-    if (memcmp((void *)g_Original, g_HookPoint, 8) == 0)
-    {
-        for (int i = 0; i < 4; i++) g_Code[2 + i] = p.bytes[i];
-        WriteCode(g_HookPoint, (void *)g_Code, 8);
-    }
+
 }
 
 // ==============================================================
@@ -616,9 +522,6 @@
     LogAlw("D3D9Client destructor called");
     SAFE_DELETE(vtab);
 
-    //Unhook
-    WriteCode(g_HookPoint, (void *)g_Original, 8);
-
     // Free constellation names memory (if allocted)
     if (g_cm_list) {
         for (DWORD n = 0; n < g_cm_list_count; ++n) {
@@ -2948,6 +2851,14 @@
         return;
     }
     SURFACE(surf)->ReleaseDC(hDC);
+}

+

+// =======================================================================

+

+bool D3D9Client::clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev)

+{

+    _TRACE;

+    return FilterElevationPhysics(hPlanet, lvl, ilat, ilng, elev_res, elev);

 }
 
 // =======================================================================
diff --git a/Orbitersdk/D3D9Client/D3D9Client.h b/Orbitersdk/D3D9Client/D3D9Client.h
--- a/Orbitersdk/D3D9Client/D3D9Client.h
+++ b/Orbitersdk/D3D9Client/D3D9Client.h
@@ -985,6 +985,23 @@
      * \todo This method should be moved into the GDIClient class
      */
     void clbkReleaseSurfaceDC (SURFHANDLE surf, HDC hDC);
+    // @}

+

+    /**

+     * \brief Filter elevation grid data

+     * \param hPlanet object handle of the planet the data belongs to

+     * \param ilat patch latitude index

+     * \param ilng patch longitude index

+     * \param lvl patch resolution level

+     * \param elev_res elevation level resolution

+     * \param elev pointer to array with elevation grid data

+     * \default None.

+     * \note Clients that manipulate elevation file data in memory (e.g. for flattening

+     *   features) for visuals should overload this method in order to manipulate the

+     *   terrain collision data in the same way. As soon as the internal collision tile

+     *   is loaded in the core, the callback is invoked.

+     */

+    virtual bool clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev);

     // @}
 

Attachments

  • Callback.txt
    8.2 KB · Views: 3
Last edited:

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
Side-note: Using "Diff" as syntax-format seems to be the preferred option for patch/diff-files...
Diff:
# HG changeset patch
# User face <[email protected]>
# Date 1627829270 -7200
#      Sun Aug 01 16:47:50 2021 +0200
# Node ID e6873b5d96eecda521cd327f92ce289e25a82567
# Parent  d2ac1f83f3961a25ad0ff4c2ec05b5c516955c0a
Flattening: exchanging hooking with API usage

diff --git a/Orbitersdk/D3D9Client/D3D9Client.cpp b/Orbitersdk/D3D9Client/D3D9Client.cpp
--- a/Orbitersdk/D3D9Client/D3D9Client.cpp
+++ b/Orbitersdk/D3D9Client/D3D9Client.cpp
@@ -231,9 +231,6 @@
     }
 };
 
-DWORD g_Hook;
-void *g_HookPoint = (void *)0x4185fc;
-void *g_TrampolineReturn = (void *)0x418604;
 std::unordered_map<OBJHANDLE, std::unordered_map<int, std::unordered_map<int, std::unordered_map<int, std::vector<FlatShape*>>>>> g_ElevationFlatteningShapes;
 std::unordered_map<OBJHANDLE, std::vector<FlatShape*>> g_ShapeStore;
 std::unordered_map<OBJHANDLE, bool> g_ShapesLoaded;
@@ -385,24 +382,8 @@
     g_ShapesLoaded[hPlanet] = true;
 }
 
-//The following array is:
-//_asm
-//{
-//    fld  qword [ebp+0x8]  // load floating point (lat or lng)
-//    fld  qword [ebp+0x10] // load floating point (lng or lat)
-//    fxch st1              // swap FPU storage
-//}
-byte g_Original[8] = { 0xdd, 0x45, 0x08, 0xdd, 0x45, 0x10, 0xd9, 0xc9 };
-
-//The following array is:
-//_asm
-//{
-//    jmp dword ptr [HOOKFUNCTION]; //jump to dynamically detected address
-//}
-byte g_Code[8] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
 template <typename Type>
-bool FilterElevation(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, Type *elev)
+bool FilterElevation(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, Type *elev)
 {
     if (!elev) return false;
     if (g_ShapesLoaded.find(hPlanet) == g_ShapesLoaded.end()) ProcessPlanetFlats(hPlanet);
@@ -436,7 +417,7 @@
             if (dist <= 1.0)
             {
                 // Do the math in INT16 basis even for "float" output
-                INT16 elevation = INT16(shape->Height);
+                INT16 elevation = INT16((double)shape->Height / elev_res);
                 if (shape->Falloff > 0)
                 {
                     for (auto pot = 0; pot < shape->Type; pot++)
@@ -446,7 +427,7 @@
                     if (dist >= 1 - shape->Falloff)
                     {
                         double alpha = (dist - 1 + shape->Falloff) / shape->Falloff;
-                        elevation = INT16(elev[i] * alpha + shape->Height * (1 - alpha));
+                        elevation = INT16(elev[i] * alpha + (double)shape->Height / elev_res * (1 - alpha));
                     }
                 }
                 // Convert to float or keep as an INT16 depending on case
@@ -458,13 +439,15 @@
 }
 
 
-void FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, INT16 *elev)
+bool FilterElevationPhysics(OBJHANDLE hPlanet, int lvl, int ilat, int ilng, double elev_res, INT16 *elev)
 {
-    if (!Config->bFlatsEnabled) return;
+    if (!Config->bFlatsEnabled) return false;
     char name[64];
-    oapiGetObjectName(hPlanet, name, 64);
-    if (FilterElevation<INT16>(hPlanet, lvl, ilat, ilng, elev))
-        LogClr("Coral", "FilterElevation[Physics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);
+    oapiGetObjectName(hPlanet, name, 64);
+    auto result = FilterElevation<INT16>(hPlanet, lvl, ilat, ilng, elev_res, elev);
+    if (result)
+        LogClr("Coral", "FilterElevation[Physics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);
+    return result;
 }
 
 
@@ -473,69 +456,10 @@
     if (!Config->bFlatsEnabled) return;
     char name[64];
     oapiGetObjectName(hPlanet, name, 64);
-    if (FilterElevation<float>(hPlanet, lvl, ilat, ilng, elev))
+    if (FilterElevation<float>(hPlanet, lvl, ilat, ilng, 1.0, elev))
         LogClr("Coral", "FilterElevation[Graphics][%s]: Level=%d, ilat=%d, ilng=%d", name, lvl, ilat, ilng);
 }
 
-void Trampoline()
-{
-    //__asm
-    //{
-    //    pop ebp                        // that's necessary because the compiler saved the pointer to the stack       
-    //    mov eax, dword ptr[edi]        // elev
-    //    mov ecx, dword ptr[esp + 0x54] // ilng
-    //    mov edx, dword ptr[esp + 0x2c] // ilat
-    //    push eax                       // arg4 = elev
-    //    mov eax, dword ptr[esp + 0x40] // internal GBody pointer
-    //    mov eax, dword ptr[eax]        // OBJHANDLE
-    //    push ecx                       // arg3 = ilng
-    //    push edx                       // arg2 = ilat
-    //    push esi                       // arg1 = lvl       
-    //    push eax                       // arg0 = OBJHANDLE
-    //    call FilterElevationPhysics       // filter function call
-    //    add esp, 14h                   // 5 arguments = 5*2 bytes = 20dec = 14h
-    //    fld qword ptr[ebp + 0x8]       // start of overwritten bytes in original position
-    //    fld qword ptr[ebp + 0x10]
-    //    fxch
-    //    jmp g_TrampolineReturn         // return from trampoline to original function
-    //}
-}
-
-int WriteCode(void *address, void *code, DWORD len)
-{
-    //Get process information
-    HANDLE hSelf = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, ::GetCurrentProcessId());
-    if (hSelf == NULL) return -1;
-    MEMORY_BASIC_INFORMATION mbi;
-
-    //Open up page of linked address   
-    if (VirtualQueryEx(hSelf, (LPVOID)address, &mbi, sizeof(mbi)) != sizeof(mbi)) return -2;
-    PVOID pvRgnBaseAddress = mbi.BaseAddress;
-    DWORD dwOldProtect1, dwOldProtect2, dwFake;
-    if (!::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect1)) return -3;
-    BOOL bStridePage = FALSE;
-    LPBYTE lpByte = (LPBYTE)pvRgnBaseAddress;
-    lpByte += 4096;
-    if ((DWORD)lpByte < (DWORD)address + 4) bStridePage = TRUE;
-    PVOID pvRgnBaseAddress2 = (LPVOID)lpByte;
-    if (bStridePage)
-        if (!::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect2))
-        {
-            ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake);
-            return -4;
-        }
-
-    //Write code
-    memcpy(address, code, len);
-
-    //Lock again
-    ::VirtualProtectEx(hSelf, pvRgnBaseAddress, 4, dwOldProtect1, &dwFake);
-    if (bStridePage) ::VirtualProtectEx(hSelf, pvRgnBaseAddress2, 4, dwOldProtect2, &dwFake);
-
-    return 0;
-}
-
-
 
 
 
@@ -588,25 +512,7 @@
     LabelPos      (0)
 
 {
-    // =====================================================================
-    // Face's terrain flattening
-    // Hook up collision loader in core Orbiter
-    // Hooks are placed recardless of "Enable Terrain Flattening", simplier that way
-    // =====================================================================
-    union
-    {
-        void *pointer;
-        byte bytes[4];
-        DWORD value;
-    } p;
-
-    g_Hook = (DWORD)(void *)Trampoline;
-    p.pointer = (void *)&g_Hook;
-    if (memcmp((void *)g_Original, g_HookPoint, 8) == 0)
-    {
-        for (int i = 0; i < 4; i++) g_Code[2 + i] = p.bytes[i];
-        WriteCode(g_HookPoint, (void *)g_Code, 8);
-    }
+   
 }
 
 // ==============================================================
@@ -616,9 +522,6 @@
     LogAlw("D3D9Client destructor called");
     SAFE_DELETE(vtab);
 
-    //Unhook
-    WriteCode(g_HookPoint, (void *)g_Original, 8);
-
     // Free constellation names memory (if allocted)
     if (g_cm_list) {
         for (DWORD n = 0; n < g_cm_list_count; ++n) {
@@ -2948,6 +2851,14 @@
         return;
     }
     SURFACE(surf)->ReleaseDC(hDC);
+}
+
+// =======================================================================
+
+bool D3D9Client::clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev)
+{
+    _TRACE;
+    return FilterElevationPhysics(hPlanet, lvl, ilat, ilng, elev_res, elev);
 }
 
 // =======================================================================
diff --git a/Orbitersdk/D3D9Client/D3D9Client.h b/Orbitersdk/D3D9Client/D3D9Client.h
--- a/Orbitersdk/D3D9Client/D3D9Client.h
+++ b/Orbitersdk/D3D9Client/D3D9Client.h
@@ -985,6 +985,23 @@
      * \todo This method should be moved into the GDIClient class
      */
     void clbkReleaseSurfaceDC (SURFHANDLE surf, HDC hDC);
+    // @}
+
+    /**
+     * \brief Filter elevation grid data
+     * \param hPlanet object handle of the planet the data belongs to
+     * \param ilat patch latitude index
+     * \param ilng patch longitude index
+     * \param lvl patch resolution level
+     * \param elev_res elevation level resolution
+     * \param elev pointer to array with elevation grid data
+     * \default None.
+     * \note Clients that manipulate elevation file data in memory (e.g. for flattening
+     *   features) for visuals should overload this method in order to manipulate the
+     *   terrain collision data in the same way. As soon as the internal collision tile
+     *   is loaded in the core, the callback is invoked.
+     */
+    virtual bool clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev);
     // @}
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Martin has kindly merged the proposal into main, so it should be possible now to activate the terrain flattening in D3D9Client's beta and x64 branches again, while also getting rid of the ugly hooking hack.
@kuddel , @jarmonik : if you need any further help with this, please drop me a note.
 

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
Thanks @Face , but the only thing I currently (desperately) need is ... time ;)
 

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,651
Reaction score
785
Points
128
Martin has kindly merged the proposal into main, so it should be possible now to activate the terrain flattening in D3D9Client's beta and x64 branches again, while also getting rid of the ugly hooking hack.
@kuddel , @jarmonik : if you need any further help with this, please drop me a note.
Ok, Thanks. I will look into it. Right now I am trying to figure out the Git. After I got the Git repository up and running then maybe you can send a "pull request". And we get the code in.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Alright, take your time with it, git is not trivial to get comfortable with. I'll graft the commits over once you are up and send a PR then.
 
Top