From cb4e0e82b76f4c9d588dc2628d1baebcf4d7cd69 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Thu, 5 Mar 2026 16:54:56 +0100 Subject: [PATCH] Basic IGGY renderer implementation, do not PR. --- 4J.Render/4J_Render.cpp | 46 ++++++++++--- .../Build/Common/UI/UIScene_Intro.cpp | 26 ++++++++ .../Build/Common/UI/UIScene_Intro.h | 4 ++ .../Build/Common/UI/UIScene_MainMenu.cpp | 13 ++++ .../Platform/Linux/Stubs/iggy_stubs.h | 64 +++++++++++++++---- .../Platform/Linux/Stubs/winapi_stubs.h | 28 +++++++- 6 files changed, 157 insertions(+), 24 deletions(-) diff --git a/4J.Render/4J_Render.cpp b/4J.Render/4J_Render.cpp index 3ba7c0f43..717345839 100644 --- a/4J.Render/4J_Render.cpp +++ b/4J.Render/4J_Render.cpp @@ -160,6 +160,14 @@ static GLenum mapPrimType(int pt) } } +// clientside awawawa +static bool isCompilingDisplayList() +{ + GLint listMode = 0; + ::glGetIntegerv(GL_LIST_MODE, &listMode); + return (listMode == GL_COMPILE || listMode == GL_COMPILE_AND_EXECUTE); +} + void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, void *dataIn, eVertexType vType, C4JRender::ePixelShaderType psType) @@ -169,19 +177,39 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, GLenum mode = mapPrimType((int)PrimitiveType); unsigned char *data = (unsigned char *)dataIn; + // Vertex layout: 3 floats pos, 2 floats tex, 4 bytes color, 4 bytes normal, 4 bytes padding = 32 bytes const int stride = 32; - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); - ::glEnableClientState(GL_COLOR_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); + if (isCompilingDisplayList()) { + // run. + ::glBegin(mode); + for (int i = 0; i < count; i++) { + unsigned char *v = data + i * stride; + float *pos = (float *)(v); + float *tex = (float *)(v + 12); + unsigned char *col = v + 20; + signed char *nrm = (signed char *)(v + 24); - ::glVertexPointer(3, GL_FLOAT, stride, data); - ::glTexCoordPointer(2, GL_FLOAT, stride, data + 12); - ::glColorPointer(4, GL_UNSIGNED_BYTE, stride, data + 20); - ::glNormalPointer(GL_BYTE, stride, data + 24); + ::glNormal3f(nrm[0] / 127.0f, nrm[1] / 127.0f, nrm[2] / 127.0f); + ::glColor4ub(col[0], col[1], col[2], col[3]); + ::glTexCoord2f(tex[0], tex[1]); + ::glVertex3f(pos[0], pos[1], pos[2]); + } + ::glEnd(); + } else { + // waiter ! fast vertex pls ! + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); + ::glEnableClientState(GL_COLOR_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); - ::glDrawArrays(mode, 0, count); + ::glVertexPointer(3, GL_FLOAT, stride, data); + ::glTexCoordPointer(2, GL_FLOAT, stride, data + 12); + ::glColorPointer(4, GL_UNSIGNED_BYTE, stride, data + 20); + ::glNormalPointer(GL_BYTE, stride, data + 24); + + ::glDrawArrays(mode, 0, count); + } } diff --git a/Minecraft.Client/Build/Common/UI/UIScene_Intro.cpp b/Minecraft.Client/Build/Common/UI/UIScene_Intro.cpp index a16729007..866c3fbc4 100644 --- a/Minecraft.Client/Build/Common/UI/UIScene_Intro.cpp +++ b/Minecraft.Client/Build/Common/UI/UIScene_Intro.cpp @@ -2,6 +2,10 @@ #include "UI.h" #include "UIScene_Intro.h" +#ifdef __linux__ +static int s_introTickCount = 0; +#endif +#include UIScene_Intro::UIScene_Intro(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { @@ -9,6 +13,9 @@ UIScene_Intro::UIScene_Intro(int iPad, void *initData, UILayer *parentLayer) : U initialiseMovie(); m_bIgnoreNavigate = false; m_bAnimationEnded = false; +#ifdef __linux__ + s_introTickCount = 0; +#endif bool bSkipESRB = false; #if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) @@ -166,3 +173,22 @@ void UIScene_Intro::handleGainFocus(bool navBack) ui.NavigateToScene(0,eUIScene_MainMenu); } } + +#ifdef __linux__ +void UIScene_Intro::tick() +{ + // Call base tick first (processes Iggy ticking) + UIScene::tick(); + + // Auto-skip the intro after 60 ticks (~2 seconds at 30fps) + // since we have no SWF renderer to play the intro animation + s_introTickCount++; + if(s_introTickCount == 60 && !m_bIgnoreNavigate) + { + fprintf(stderr, "[Linux] Auto-skipping intro -> MainMenu after %d ticks\n", s_introTickCount); + m_bIgnoreNavigate = true; + // Skip straight to MainMenu, bypassing SaveMessage (no SWF interaction possible) + ui.NavigateToScene(0, eUIScene_MainMenu); + } +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Build/Common/UI/UIScene_Intro.h b/Minecraft.Client/Build/Common/UI/UIScene_Intro.h index 8bdc030e5..dbdc72613 100644 --- a/Minecraft.Client/Build/Common/UI/UIScene_Intro.h +++ b/Minecraft.Client/Build/Common/UI/UIScene_Intro.h @@ -45,6 +45,10 @@ public: virtual void handleAnimationEnd(); virtual void handleGainFocus(bool navBack); +#ifdef __linux__ + virtual void tick(); +#endif + #ifdef __PSVITA__ virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); #endif diff --git a/Minecraft.Client/Build/Common/UI/UIScene_MainMenu.cpp b/Minecraft.Client/Build/Common/UI/UIScene_MainMenu.cpp index 26f2e6276..f9b825499 100644 --- a/Minecraft.Client/Build/Common/UI/UIScene_MainMenu.cpp +++ b/Minecraft.Client/Build/Common/UI/UIScene_MainMenu.cpp @@ -1802,6 +1802,19 @@ void UIScene_MainMenu::tick() { UIScene::tick(); +#ifdef __linux__ + { + static int s_mainMenuTickCount = 0; + s_mainMenuTickCount++; + if(s_mainMenuTickCount == 90) // ~3 seconds at 30fps + { + fprintf(stderr, "[Linux] Auto-starting trial world from MainMenu after %d ticks\n", s_mainMenuTickCount); + LoadTrial(); + return; + } + } +#endif + #if defined(__PS3__) || defined (__ORBIS__) || defined(__PSVITA__) if(m_bLaunchFullVersionPurchase) { diff --git a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h index 532c4b265..41b5e0f1a 100644 --- a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h +++ b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h @@ -36,29 +36,55 @@ RADEXPFUNC inline void RADEXPLINK IggyPlayerDrawTilesEnd(Iggy *f) { STUBBED; } -static int thing = 0; +// Each fake Iggy player gets its own state block +struct FakeIggyPlayer { + int tickCount; + bool needsTick; + IggyProperties props; + void *userdata; +}; + +// Simple player pool +static FakeIggyPlayer s_fakePlayers[64]; +static int s_fakePlayerCount = 0; RADEXPFUNC inline Iggy * RADEXPLINK IggyPlayerCreateFromMemory( void const * data, U32 data_size_in_bytes, IggyPlayerConfig *config) { - STUBBED; - return (Iggy*)&thing; + if(s_fakePlayerCount >= 64) return nullptr; + FakeIggyPlayer *fp = &s_fakePlayers[s_fakePlayerCount++]; + fp->tickCount = 0; + fp->needsTick = true; + fp->userdata = nullptr; + // Default to 1920x1080 at 30fps + memset(&fp->props, 0, sizeof(fp->props)); + fp->props.movie_width_in_pixels = 1920; + fp->props.movie_height_in_pixels = 1080; + fp->props.movie_frame_rate_from_file_in_fps = 30.0f; + fp->props.movie_frame_rate_current_in_fps = 30.0f; + fprintf(stderr, "[Iggy Stub] Created fake player %d (data=%p, size=%u)\n", s_fakePlayerCount-1, data, data_size_in_bytes); + return (Iggy*)fp; } +static FakeIggyPlayer* getFakePlayer(Iggy *player) { + return (FakeIggyPlayer*)player; +} RADEXPFUNC inline void RADEXPLINK IggyPlayerInitializeAndTickRS(Iggy *player) { - STUBBED; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp) { fp->tickCount = 0; fp->needsTick = true; } } -static IggyProperties properties; - RADEXPFUNC inline IggyProperties * RADEXPLINK IggyPlayerProperties(Iggy *player) { - STUBBED; - return &properties; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp) return &fp->props; + static IggyProperties defaultProps = {}; + return &defaultProps; } RADEXPFUNC inline void RADEXPLINK IggyPlayerSetUserdata(Iggy *player, void *userdata) { - STUBBED; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp) fp->userdata = userdata; } RADEXPFUNC inline IggyName RADEXPLINK IggyPlayerCreateFastName(Iggy *f, IggyUTF16 const *name, S32 len) { STUBBED; @@ -69,14 +95,22 @@ RADEXPFUNC inline rrbool RADEXPLINK IggyDebugGetMemoryUseInfo(Iggy *player, Iggy return false; } RADEXPFUNC inline rrbool RADEXPLINK IggyPlayerReadyToTick(Iggy *player) { - STUBBED; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp && fp->needsTick) return true; return false; } RADEXPFUNC inline void RADEXPLINK IggyPlayerTickRS(Iggy *player) { - STUBBED; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp) { + fp->tickCount++; + // Allow one tick per frame cycle + fp->needsTick = false; + } } RADEXPFUNC inline void RADEXPLINK IggyPlayerDraw(Iggy *f) { - STUBBED; + // Re-arm tick for next frame + FakeIggyPlayer *fp = getFakePlayer(f); + if(fp) fp->needsTick = true; } RADEXPFUNC inline void RADEXPLINK IggyMakeEventKey(IggyEvent *event, IggyKeyevent event_type, IggyKeycode keycode, IggyKeyloc keyloc) { STUBBED; @@ -144,7 +178,8 @@ RADEXPFUNC inline void RADEXPLINK IggySetTextureSubstitutionCallbacks(Iggy_Textu STUBBED; } RADEXPFUNC inline void * RADEXPLINK IggyPlayerGetUserdata(Iggy *player) { - STUBBED; + FakeIggyPlayer *fp = getFakePlayer(player); + if(fp) return fp->userdata; return 0; } RADEXPFUNC inline IggyLibrary RADEXPLINK IggyLibraryCreateFromMemoryUTF16( @@ -186,4 +221,7 @@ RADEXPFUNC inline void RADEXPLINK IggyInstallPerfmon(void *perfmon) { STUBBED; } +// GDraw memory/warning functions are defined in gdraw_glfw.c (C linkage) +// Juicey you stupid idiot do NOT define them here + #endif // IGGYSTUBS_H \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Stubs/winapi_stubs.h b/Minecraft.Client/Platform/Linux/Stubs/winapi_stubs.h index 83e0d0a4b..7e5ab1315 100644 --- a/Minecraft.Client/Platform/Linux/Stubs/winapi_stubs.h +++ b/Minecraft.Client/Platform/Linux/Stubs/winapi_stubs.h @@ -5,6 +5,7 @@ #include #include +#include #define TRUE true #define FALSE false @@ -967,11 +968,34 @@ static inline int swprintf_s(wchar_t* buf, size_t sz, const wchar_t* fmt, ...) { static inline HMODULE GetModuleHandle(LPCSTR lpModuleName) { return 0; } static inline LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { - assert(0 && "FIXME: implement VirtualAlloc"); + // MEM_COMMIT | MEM_RESERVE → mmap anonymous + int prot = 0; + if (flProtect == 0x04 /*PAGE_READWRITE*/) prot = PROT_READ | PROT_WRITE; + else if (flProtect == 0x40 /*PAGE_EXECUTE_READWRITE*/) prot = PROT_READ | PROT_WRITE | PROT_EXEC; + else if (flProtect == 0x02 /*PAGE_READONLY*/) prot = PROT_READ; + else prot = PROT_READ | PROT_WRITE; // default + + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + if (lpAddress != NULL) flags |= MAP_FIXED; + + void *p = mmap(lpAddress, dwSize, prot, flags, -1, 0); + if (p == MAP_FAILED) return NULL; + return p; } static inline BOOL VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) { - assert(0 && "FIXME: implement VirtualFree"); + if (lpAddress == NULL) return FALSE; + // MEM_RELEASE (0x8000) frees the whole region + if (dwFreeType == 0x8000 /*MEM_RELEASE*/) { + // dwSize should be 0 for MEM_RELEASE per Win32 API, but we don't track allocation sizes + // Use dwSize if provided, otherwise this is a best-effort + if (dwSize == 0) dwSize = 4096; // minimum page + munmap(lpAddress, dwSize); + } else { + // MEM_DECOMMIT (0x4000) - just decommit (make inaccessible) + madvise(lpAddress, dwSize, MADV_DONTNEED); + } + return TRUE; } #define swscanf_s swscanf