diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index accb6efe..32eb7869 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -42,11 +42,11 @@ static const char* defaultVertexShaderSource = "layout(location = 0) in vec2 aPos;\n" "layout(location = 1) in vec4 aColor;\n" "layout(location = 2) in vec2 aTexCoord;\n" - "uniform mat4 uProjection;\n" + "uniform mat4 uWorldViewProjection;\n" "out vec2 vTexCoord;\n" "out vec4 vColor;\n" "void main() {\n" - " gl_Position = uProjection * vec4(aPos, 0.0, 1.0);\n" + " gl_Position = uWorldViewProjection * vec4(aPos, 0.0, 1.0);\n" " vTexCoord = aTexCoord;\n" " vColor = aColor;\n" "}\n"; @@ -250,6 +250,10 @@ static void glInit(Renderer* renderer, DataWin* dataWin) { GLRenderer* gl = (GLRenderer*) renderer; renderer->dataWin = dataWin; + Matrix4f World; + Matrix4f_identity(&World); + renderer->gmlMatrices[MATRIX_WORLD] = World; + GMLShader* defaultShader = safeCalloc(1, sizeof(GMLShader)); bool success = compileProgram(defaultShader, "default", defaultVertexShaderSource, defaultFragmentShaderSource, 0, nullptr); if (!success) { @@ -400,8 +404,14 @@ static void glGpuSetShader(Renderer* renderer, int32_t shaderIndex) { GLShaderUniform* gmAlphaTestEnabledUniform = findShaderUniformByName(gmlShader, "gm_AlphaTestEnabled"); GLShaderUniform* gmAlphaRefValue = findShaderUniformByName(gmlShader, "gm_AlphaRefValue"); + Matrix4f FlippedClip[MATRICES_MAX]; + for (int32_t i = 0; i < MATRICES_MAX; i++) { + FlippedClip[i] = renderer->gmlMatrices[i]; + Matrix4f_flipClipY(&FlippedClip[i]); + } + if (gmMatricesUniform != nullptr) { - glUniformMatrix4fv(gmMatricesUniform->location, 5, GL_FALSE, renderer->gmlMatrices[0].m); + glUniformMatrix4fv(gmMatricesUniform->location, 5, GL_FALSE, FlippedClip[0].m); } if (gmFogColourUniform != nullptr) { glUniform1i(gmFogColourUniform->location, gl->fogColor); @@ -428,13 +438,18 @@ static void glShaderSettingsRefresh(Renderer* renderer) { glUseProgram(gl->defaultShaderProgram->shaderId); - GLShaderUniform* uProjection = findShaderUniformByName(gl->defaultShaderProgram, "uProjection"); + GLShaderUniform* uWorldViewProjection = findShaderUniformByName(gl->defaultShaderProgram, "uWorldViewProjection"); GLShaderUniform* uFogColor = findShaderUniformByName(gl->defaultShaderProgram, "uFogColor"); GLShaderUniform* uAlphaTestRef = findShaderUniformByName(gl->defaultShaderProgram, "uAlphaTestRef"); GLShaderUniform* uAlphaTestEnabled = findShaderUniformByName(gl->defaultShaderProgram, "uAlphaTestEnabled"); GLShaderUniform* uTexture = findShaderUniformByName(gl->defaultShaderProgram, "uTexture"); + Matrix4f FlippedClip[MATRICES_MAX]; + for (int32_t i = 0; i < MATRICES_MAX; i++) { + FlippedClip[i] = renderer->gmlMatrices[i]; + Matrix4f_flipClipY(&FlippedClip[i]); + } - glUniformMatrix4fv(uProjection->location, 1, GL_FALSE, renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION].m); + glUniformMatrix4fv(uWorldViewProjection->location, 1, GL_FALSE, FlippedClip[MATRIX_WORLD_VIEW_PROJECTION].m); glUniform4f(uFogColor->location, fogR, fogG, fogB, gl->fogEnable ? 1.0f : 0.0f); glUniform1f(uAlphaTestRef->location, gl->alphaTestRef); glUniform1i(uAlphaTestEnabled->location, gl->alphaTestEnable); @@ -442,6 +457,31 @@ static void glShaderSettingsRefresh(Renderer* renderer) { } } +// camera_apply: swap the active world->clip projection on the current target without touching its viewport. +static void glApplyProjection(Renderer* renderer, const Matrix4f* ViewMatrix,const Matrix4f* ProjectionMatrix) { + GLRenderer* gl = (GLRenderer*) renderer; + + // Flush first so pending quads draw under the projection they were issued with. + flushBatch(gl); + + Matrix4f World = renderer->gmlMatrices[MATRIX_WORLD]; + Matrix4f View = *ViewMatrix; + Matrix4f Projection = *ProjectionMatrix; + + Matrix4f WorldView; + Matrix4f_multiply(&WorldView, &View, &World); + + Matrix4f WorldViewProjection; + Matrix4f_multiply(&WorldViewProjection, &Projection, &WorldView); + + renderer->gmlMatrices[MATRIX_VIEW] = View; + renderer->gmlMatrices[MATRIX_PROJECTION] = Projection; + renderer->gmlMatrices[MATRIX_WORLD_VIEW] = WorldView; + renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = WorldViewProjection; + //oh my I hope it's good enough. + glShaderSettingsRefresh(renderer); +} + static void glGpuResetShader(Renderer* renderer) { GLRenderer* gl = (GLRenderer*) renderer; flushBatch(gl); @@ -515,7 +555,7 @@ static void glBeginFrame(Renderer* renderer, int32_t gameW, int32_t gameH, int32 gl->base.CPortH = gameH; } -static void glBeginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, float viewAngle) { +static void glBeginView(Renderer* renderer, MAYBE_UNUSED int32_t viewX, MAYBE_UNUSED int32_t viewY, MAYBE_UNUSED int32_t viewW, MAYBE_UNUSED int32_t viewH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, MAYBE_UNUSED float viewAngle) { GLRenderer* gl = (GLRenderer*) renderer; gl->batchCount = 0; @@ -524,29 +564,30 @@ static void glBeginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_ // Set viewport and scissor to the port rectangle within the FBO // FBO uses game resolution, port coordinates are in game space // OpenGL viewport Y is bottom-up, game Y is top-down - int32_t glPortY = gl->gameH - portY - portH; - glViewport(portX, glPortY, portW, portH); + + glViewport(portX, portY, portW, portH); gl->base.CPortX = portX; - gl->base.CPortY = glPortY; + gl->base.CPortY = portY; gl->base.CPortW = portW; gl->base.CPortH = portH; glEnable(GL_SCISSOR_TEST); - glScissor(portX, glPortY, portW, portH); + glScissor(portX, portY, portW, portH); - // World -> clip transform for this view. - Matrix4f projection; - Matrix4f_viewProjection(&projection, (float) viewX, (float) viewY, (float) viewW, (float) viewH, viewAngle); - Matrix4f_flipClipY(&projection); + int32_t ViewCurrent = 0; + if (renderer->runner->viewsEnabled) { + ViewCurrent = renderer->runner->viewCurrent; + } + RuntimeView* view = &renderer->runner->views[ViewCurrent]; + gl->base.CameraCurrent = view->cameraId; + GMLCamera* camera = Runner_getCameraById(renderer->runner, gl->base.CameraCurrent); + glApplyProjection(renderer,&camera->ViewMatrix,&camera->ProjectionMatrix); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; glShaderSettingsRefresh(renderer); glActiveTexture(GL_TEXTURE1); glBindVertexArray(gl->vao); - renderer->previousViewMatrix = projection; - } static void glEndView(Renderer* renderer) { @@ -555,19 +596,7 @@ static void glEndView(Renderer* renderer) { glDisable(GL_SCISSOR_TEST); } -// camera_apply: swap the active world->clip projection on the current target without touching its viewport. -static void glApplyProjection(Renderer* renderer, const Matrix4f* worldToClip) { - GLRenderer* gl = (GLRenderer*) renderer; - // Flush first so pending quads draw under the projection they were issued with. - flushBatch(gl); - Matrix4f projection = *worldToClip; - Matrix4f_flipClipY(&projection); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; - glShaderSettingsRefresh(renderer); - renderer->previousViewMatrix = projection; -} - -static void glBeginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) { +static void glBeginGUI(Renderer* renderer, MAYBE_UNUSED int32_t guiW, MAYBE_UNUSED int32_t guiH, int32_t portX, int32_t portY, int32_t portW, MAYBE_UNUSED int32_t portH, int32_t targetSurfaceId) { GLRenderer* gl = (GLRenderer*) renderer; gl->batchCount = 0; @@ -587,27 +616,70 @@ static void glBeginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t p } glEnable(GL_SCISSOR_TEST); + //I dunno hopefully this is at least somewhat correct... + gl->base.CameraCurrent = GUI_CAMERA; + GMLCamera* camera = &renderer->runner->guiCamera; + camera->allocated = true; + camera->viewX = 0.0; + camera->viewY = 0.0; + camera->viewWidth = guiW; + camera->viewHeight = guiH; + camera->borderX = 0; + camera->borderY = 0; + camera->speedX = 0; + camera->speedY = 0; + camera->objectId = -1; + camera->viewAngle = 0; + + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) guiW, (float) guiH, 32000.0, 0.0); + + Matrix4f ViewMatrix; + float x = (float) guiW * 0.5f; + float y = (float) guiH * 0.5f; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + camera->ViewMatrix = ViewMatrix; + camera->ProjectionMatrix = ProjectionMatrix; + glApplyProjection(renderer,&camera->ViewMatrix,&camera->ProjectionMatrix); - Matrix4f projection; - Matrix4f_guiProjection(&projection, (float) guiW, (float) guiH, (float) portW, (float) portH); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; - glShaderSettingsRefresh(renderer); glActiveTexture(GL_TEXTURE1); glBindVertexArray(gl->vao); } -static void glSetGuiProjection(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portW, int32_t portH, bool renderingToUserSurface) { +static void glSetGuiProjection(Renderer* renderer, int32_t guiW, int32_t guiH, MAYBE_UNUSED int32_t portW, MAYBE_UNUSED int32_t portH, MAYBE_UNUSED bool renderingToUserSurface) { GLRenderer* gl = (GLRenderer*) renderer; flushBatch(gl); - Matrix4f projection; - Matrix4f_guiProjection(&projection, (float) guiW, (float) guiH, (float) portW, (float) portH); + // GL surfaces are stored bottom-up and draw_surface samples them with vertical flip. - // Flip the projection when we are rendering to a user surface so it comes back upright. - if (renderingToUserSurface) Matrix4f_flipClipY(&projection); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; - glShaderSettingsRefresh(renderer); + gl->base.CameraCurrent = GUI_CAMERA; + GMLCamera* camera = &renderer->runner->guiCamera; + camera->allocated = true; + camera->viewX = 0.0; + camera->viewY = 0.0; + camera->viewWidth = guiW; + camera->viewHeight = guiH; + camera->borderX = 0; + camera->borderY = 0; + camera->speedX = 0; + camera->speedY = 0; + camera->objectId = -1; + camera->viewAngle = 0; + + //yeah no I have no idea how to do the GUI + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) guiW, (float) guiH, 32000.0, 0.0); + if (renderingToUserSurface) Matrix4f_flipClipY(&ProjectionMatrix); + Matrix4f ViewMatrix; + float x = (float) guiW * 0.5f; + float y = (float) guiH * 0.5f; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + camera->ViewMatrix = ViewMatrix; + camera->ProjectionMatrix = ProjectionMatrix; + glApplyProjection(renderer,&camera->ViewMatrix,&camera->ProjectionMatrix); } static void glEndGUI(Renderer* renderer) { @@ -652,11 +724,10 @@ static void glClearScreen(Renderer* renderer, uint32_t color, float alpha) { float b = (float) BGR_B(color) / 255.0f; // GML draw_clear ignores the active scissor and clears the whole target. Disable scissor for the clear and restore it after. - GLboolean scissorWasEnabled = glIsEnabled(GL_SCISSOR_TEST); - if (scissorWasEnabled) glDisable(GL_SCISSOR_TEST); + //No it doesn't? glClearColor(r, g, b, alpha); glClear(GL_COLOR_BUFFER_BIT); - if (scissorWasEnabled) glEnable(GL_SCISSOR_TEST); + } // Lazily decodes and uploads a TXTR page on first access. @@ -1971,6 +2042,15 @@ static bool glSurfaceGetPixels(Renderer* renderer, int32_t surfaceId, uint8_t* o static bool glSetRenderTarget(Renderer* renderer, int32_t surfaceId, bool implicitApplicationSurface) { GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + + int32_t ViewCurrent = 0; + if (renderer->runner->viewsEnabled) { + ViewCurrent = renderer->runner->viewCurrent; + } + RuntimeView* view = &renderer->runner->views[ViewCurrent]; + gl->base.CameraCurrent = view->cameraId; + GMLCamera* camera = Runner_getCameraById(renderer->runner, gl->base.CameraCurrent); if (0 > surfaceId || (uint32_t) surfaceId >= gl->surfaceCount) return false; if (gl->surfaces[surfaceId] == 0) return false; @@ -1980,19 +2060,56 @@ static bool glSetRenderTarget(Renderer* renderer, int32_t surfaceId, bool implic if (surfaceId == renderer->runner->applicationSurfaceId && implicitApplicationSurface) { glViewport(gl->base.CPortX, gl->base.CPortY, gl->base.CPortW, gl->base.CPortH); glEnable(GL_SCISSOR_TEST); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = renderer->previousViewMatrix; - glShaderSettingsRefresh(renderer); + + glApplyProjection(renderer,&camera->ViewMatrix,&camera->ProjectionMatrix); + return true; } - // Normal surface bind: surface-local ortho covering the whole surface, no scissor. - Matrix4f projection; - Matrix4f_identity(&projection); - Matrix4f_ortho(&projection, 0.0f, (float) gl->surfaceWidth[surfaceId], 0.0f, (float) gl->surfaceHeight[surfaceId], -1.0f, 1.0f); + + if (surfaceId == view->surfaceId) { + //the surface belongs to the view we are rending, we use the view's camera. + glViewport(0, 0, gl->surfaceWidth[surfaceId], gl->surfaceHeight[surfaceId]); + glDisable(GL_SCISSOR_TEST); + glApplyProjection(renderer,&camera->ViewMatrix,&camera->ProjectionMatrix); + return true; + } else { + //camera will use full surface. + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) gl->surfaceWidth[surfaceId], -((float) gl->surfaceHeight[surfaceId]), 32000.0, 0.0); + + Matrix4f ViewMatrix; + float x = (float) gl->surfaceWidth[surfaceId] * 0.5f; + float y = (float) gl->surfaceHeight[surfaceId] * 0.5f; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + gl->base.CameraCurrent = SURFACE_CAMERA; + + GMLCamera* camera = &renderer->runner->surfaceCamera; + + camera->allocated = true; + camera->viewX = 0.0; + camera->viewY = 0.0; + camera->viewWidth = gl->surfaceWidth[surfaceId]; + camera->viewHeight = gl->surfaceHeight[surfaceId]; + camera->borderX = 0; + camera->borderY = 0; + camera->speedX = 0; + camera->speedY = 0; + camera->objectId = -1; + camera->viewAngle = 0; + + camera->ProjectionMatrix = ProjectionMatrix; + camera->ViewMatrix = ViewMatrix; + glViewport(0, 0, gl->surfaceWidth[surfaceId], gl->surfaceHeight[surfaceId]); + glDisable(GL_SCISSOR_TEST); + glApplyProjection(renderer, &ViewMatrix,&ProjectionMatrix); + return true; + } + + glViewport(0, 0, gl->surfaceWidth[surfaceId], gl->surfaceHeight[surfaceId]); glDisable(GL_SCISSOR_TEST); - renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; - glShaderSettingsRefresh(renderer); return true; } @@ -2480,6 +2597,29 @@ static bool glShadersSupported(void) { return true; } +static void glSetMatrix(Renderer* renderer, int32_t MatrixType, Matrix4f Matrix) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + renderer->gmlMatrices[MatrixType] = Matrix; + //yeah just recalculate everything when we change a matrix + //TODO LATR: only allow these 3 to be changed directly, other ones should only be allowed to be calculated by the rest of the function + Matrix4f World = renderer->gmlMatrices[MATRIX_WORLD]; + Matrix4f View = renderer->gmlMatrices[MATRIX_VIEW]; + Matrix4f Projection = renderer->gmlMatrices[MATRIX_PROJECTION]; + + Matrix4f WorldView; + Matrix4f_multiply(&WorldView, &View, &World); + + Matrix4f WorldViewProjection; + Matrix4f_multiply(&WorldViewProjection, &Projection, &WorldView); + + renderer->gmlMatrices[MATRIX_WORLD_VIEW] = WorldView; + renderer->gmlMatrices[MATRIX_WORLD_VIEW_PROJECTION] = WorldViewProjection; + + + glShaderSettingsRefresh(renderer); +} + // ===[ Vtable ]=== static RendererVtable glVtable; @@ -2552,6 +2692,7 @@ Renderer* GLRenderer_create(void) { glVtable.textureSetStage = glTextureSetStage, glVtable.shaderIsCompiled = glShaderIsCompiled, glVtable.shadersSupported = glShadersSupported, + glVtable.setMatrix = glSetMatrix, gl->base.drawColor = 0xFFFFFF; // white (BGR) gl->base.drawAlpha = 1.0f; @@ -2560,5 +2701,6 @@ Renderer* GLRenderer_create(void) { gl->base.drawValign = 0; gl->base.circlePrecision = 24; gl->base.currentShader = -1; + gl->base.CameraCurrent = 0; return (Renderer*) gl; } diff --git a/src/gl_legacy/gl_legacy_renderer.c b/src/gl_legacy/gl_legacy_renderer.c index cbba9f0a..b7437eae 100644 --- a/src/gl_legacy/gl_legacy_renderer.c +++ b/src/gl_legacy/gl_legacy_renderer.c @@ -186,8 +186,8 @@ static void glEndView(MAYBE_UNUSED Renderer* renderer) { } // camera_apply: swap the active world->clip projection on the current target without touching its viewport. -static void glApplyProjection(Renderer* renderer, const Matrix4f* worldToClip) { - Matrix4f projection = *worldToClip; +static void glApplyProjection(Renderer* renderer, MAYBE_UNUSED const Matrix4f* ViewMatrix, MAYBE_UNUSED const Matrix4f* ProjectionMatrix) { + Matrix4f projection = *ProjectionMatrix; //fix it later Matrix4f_flipClipY(&projection); glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection.m); diff --git a/src/matrix_math.h b/src/matrix_math.h index ad8acd77..7c15807a 100644 --- a/src/matrix_math.h +++ b/src/matrix_math.h @@ -51,6 +51,87 @@ static inline Matrix4f* Matrix4f_multiply(Matrix4f* dest, const Matrix4f* a, con return dest; } +static inline Matrix4f* Matrix4f_LookAt(Matrix4f* dest, float x_from, float y_from, float z_from, float x_to, float y_to, float z_to, float x_up, float y_up, float z_up) { + + double xFrom = x_from; + double yFrom = y_from; + double zFrom = z_from; + + double xTo = x_to; + double yTo = y_to; + double zTo = z_to; + + double xUp = x_up; + double yUp = y_up; + double zUp = z_up; + double magUp = sqrt(xUp * xUp + yUp * yUp + zUp * zUp); + xUp /= magUp; + yUp /= magUp; + zUp /= magUp; + + double xLook = xTo - xFrom; + double yLook = yTo - yFrom; + double zLook = zTo - zFrom; + double magLook = sqrt(xLook * xLook + yLook * yLook + zLook * zLook); + xLook /= magLook; + yLook /= magLook; + zLook /= magLook; + + // normalised cross product between Up and Look + double xRight = yUp * zLook - zUp * yLook; + double yRight = zUp * xLook - xUp * zLook; + double zRight = xUp * yLook - yUp * xLook; + double magRight = sqrt(xRight * xRight + yRight * yRight + zRight * zRight); + xRight /= magRight; + yRight /= magRight; + zRight /= magRight; + + // normalised cross product between Look and Right + xUp = yLook * zRight - zLook * yRight; + yUp = zLook * xRight - xLook * zRight; + zUp = xLook * yRight - yLook * xRight; + magUp = sqrt(xUp * xUp + yUp * yUp + zUp * zUp); + xUp /= magUp; + yUp /= magUp; + zUp /= magUp; + + double x, y, z; + x = xFrom * xRight + yFrom * yRight + zFrom * zRight; + y = xFrom * xUp + yFrom * yUp + zFrom * zUp; + z = xFrom * xLook + yFrom * yLook + zFrom * zLook; + + dest->m[Matrix_getIndex(0, 0)] = xRight; + dest->m[Matrix_getIndex(1, 0)] = xUp; + dest->m[Matrix_getIndex(2, 0)] = xLook; + + dest->m[Matrix_getIndex(0, 1)] = yRight; + dest->m[Matrix_getIndex(1, 1)] = yUp; + dest->m[Matrix_getIndex(2, 1)] = yLook; + + dest->m[Matrix_getIndex(0, 2)] = zRight; + dest->m[Matrix_getIndex(1, 2)] = zUp; + dest->m[Matrix_getIndex(2, 2)] = zLook; + + dest->m[Matrix_getIndex(0, 3)] = -x; + dest->m[Matrix_getIndex(1, 3)] = -y; + dest->m[Matrix_getIndex(2, 3)] = -z; + + return dest; +} + +static inline Matrix4f* Matrix4f_Orthographic(Matrix4f* dest, float width, float height, float zfar, float znear) { + + memset(dest->m, 0, sizeof(dest->m)); + dest->m[Matrix_getIndex(0,0)] = 2.0f / width; + dest->m[Matrix_getIndex(1,1)] = 2.0f / height; + dest->m[Matrix_getIndex(2,2)] = 1.0f / (zfar - znear); + dest->m[Matrix_getIndex(3,3)] = 1.0f; + + dest->m[Matrix_getIndex(2,3)] = znear / (znear - zfar); + + return dest; +} + // ===[ Orthographic Projection ]=== // Post-multiply orthographic projection onto dest: dest = dest * ortho(l, r, b, t, n, f) diff --git a/src/renderer.h b/src/renderer.h index a7f39023..59d42234 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -40,6 +40,9 @@ #define MAX_VS_LIGHTS 8 #define MAX_TEXTURE_STAGES 8 +//these 2 IDs are just IDs I simply made up, replace them with proper ones eventually oki? +#define GUI_CAMERA 4096 +#define SURFACE_CAMERA 8192 // Sentinel returned by ensureApplicationSurface on platforms that don't back the application_surface with a real entry in the renderer's surface table. // @@ -72,7 +75,7 @@ typedef struct { void (*endFrameEnd)(Renderer* renderer); void (*beginView)(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, float viewAngle); void (*endView)(Renderer* renderer); - void (*applyProjection)(Renderer* renderer, const Matrix4f* worldToClip); + void (*applyProjection)(Renderer* renderer, const Matrix4f* ViewMatrix,const Matrix4f* ProjectionMatrix); // GUI pass: coordinates are (0,0)..(guiW,guiH) mapped to the current view's port rect. Called after endView. // targetSurfaceId is the surface the pass renders into, or RENDER_TARGET_HOST_FRAMEBUFFER. void (*beginGUI)(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId); @@ -149,6 +152,7 @@ typedef struct { void (*textureSetStage)(Renderer* renderer, int32_t slot, uint32_t texID); bool (*shaderIsCompiled)(Renderer* renderer, int32_t shader); bool (*shadersSupported)(void); + void (*setMatrix)(Renderer* renderer, int32_t MatrixType, Matrix4f Matrix); } RendererVtable; // ===[ Renderer Base Struct ]=== @@ -163,7 +167,7 @@ struct Renderer { int32_t drawValign; // 0=top, 1=middle, 2=bottom int32_t circlePrecision; // segments used by draw_circle/draw_ellipse, clamped to [4, 64] and rounded down to multiple of 4. Default 24. //It's The Simplest Way I Found To Restore Previous Thingies For Rendering SORRY - Matrix4f previousViewMatrix; + Matrix4f previousViewMatrix; //when you go fix the Legacy OpenGL renderer, please remove this, as we don't need this anymore I hope int32_t CPortX; int32_t CPortY; int32_t CPortW; @@ -171,6 +175,7 @@ struct Renderer { Runner* runner; Matrix4f gmlMatrices[MATRICES_MAX]; int32_t currentShader; + int32_t CameraCurrent; }; // ===[ Shared Helpers (platform-agnostic) ]=== diff --git a/src/runner.c b/src/runner.c index 450122e2..82863065 100644 --- a/src/runner.c +++ b/src/runner.c @@ -1136,11 +1136,16 @@ void Runner_drawViews(Runner* runner, int32_t gameW, int32_t gameH, bool debugSh if (runner->drawBackgroundColor) renderer->vtable->clearScreen(renderer, runner->currentRoom->backgroundColor, 1.0f); - Matrix4f proj; - Matrix4f_viewProjection(&proj, (float) camera->viewX, (float) camera->viewY, (float) camera->viewWidth, (float) camera->viewHeight, camera->viewAngle); - renderer->vtable->applyProjection(renderer, &proj); + Matrix4f ViewMatrix = camera->ViewMatrix; + Matrix4f ProjectionMatrix = camera->ProjectionMatrix; + runner->renderer->vtable->applyProjection(runner->renderer, &ViewMatrix, &ProjectionMatrix); + runner->viewCurrent = (int32_t) vi; + runner->renderer->CameraCurrent = runner->views[runner->viewCurrent].cameraId; + runner->renderer->vtable->applyProjection(runner->renderer, &ViewMatrix, &ProjectionMatrix); + + Runner_draw(runner); renderer->vtable->flush(renderer); @@ -1161,6 +1166,7 @@ void Runner_drawViews(Runner* runner, int32_t gameW, int32_t gameH, bool debugSh float viewAngle = camera->viewAngle; runner->viewCurrent = (int32_t) vi; + runner->renderer->CameraCurrent = runner->views[runner->viewCurrent].cameraId; renderer->vtable->beginView(renderer, viewX, viewY, viewW, viewH, portX, portY, portW, portH, viewAngle); Runner_draw(runner); @@ -1174,13 +1180,38 @@ void Runner_drawViews(Runner* runner, int32_t gameW, int32_t gameH, bool debugSh } if (!anyViewRendered) { - // See GameMaker-HTML5's "DrawViews", in specific the !m_enableviews path - // When views aren't used, the room width/height is used - int32_t viewX, viewY, viewW, viewH; - expandViewAxis(0, (int32_t) runner->currentRoom->width, gameW, widescreenBaseW, &viewX, &viewW); - expandViewAxis(0, (int32_t) runner->currentRoom->height, gameH, widescreenBaseH, &viewY, &viewH); - applyFreeCamera(runner, &viewX, &viewY, &viewW, &viewH); - renderer->vtable->beginView(renderer, viewX, viewY, viewW, viewH, 0, 0, gameW, gameH, 0); + // No views enabled: render with default full-screen view. + // gameW/gameH already include the widescreen extra, shift the world origin by half of it on each grown axis so the original room stays centered and the revealed area is split evenly between the opposing edges. + runner->viewCurrent = 0; + GMLCamera* camera = Runner_getCameraForView(runner, (int32_t) runner->viewCurrent); + runner->renderer->CameraCurrent = runner->views[runner->viewCurrent].cameraId; + int32_t fullViewX = -(runner->widescreenExtraWidth / 2); + int32_t fullViewY = -(runner->widescreenExtraHeight / 2); + int32_t fullViewW = gameW; + int32_t fullViewH = gameH; + applyFreeCamera(runner, &fullViewX, &fullViewY, &fullViewW, &fullViewH); + + //make default projection + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) runner->currentRoom->width, -((float) runner->currentRoom->height), 32000.0, 0.0); + + Matrix4f ViewMatrix; + float x = (float) runner->currentRoom->width * 0.5f; + float y = (float) runner->currentRoom->height * 0.5f; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + if (camera != nullptr) { + camera->viewX = 0; + camera->viewY = 0; + camera->viewWidth = runner->currentRoom->width; + camera->viewHeight = runner->currentRoom->height; + camera->viewAngle = 0.0; + camera->ViewMatrix = ViewMatrix; + camera->ProjectionMatrix = ProjectionMatrix; + } + + renderer->vtable->beginView(renderer, fullViewX, fullViewY, fullViewW, fullViewH, 0, 0, gameW, gameH, 0.0f); + Runner_draw(runner); if (debugShowCollisionMasks) DebugOverlay_drawCollisionMasks(runner); @@ -1191,6 +1222,7 @@ void Runner_drawViews(Runner* runner, int32_t gameW, int32_t gameH, bool debugSh // Reset view_current to 0 so non-Draw events (Step, Alarm, Create) see view_current = 0 runner->viewCurrent = 0; + runner->renderer->CameraCurrent = runner->views[runner->viewCurrent].cameraId; } // ===[ Instance Creation Helper ]=== @@ -1292,6 +1324,8 @@ GMLCamera* Runner_getCameraById(Runner* runner, int32_t id) { if (0 > id) return nullptr; else if (MAX_DEFAULT_ROOM_CAMERAS > id) camera = &runner->defaultCameras[id]; else if (MAX_CAMERAS > id) camera = &runner->userCameras[id - MAX_DEFAULT_ROOM_CAMERAS]; + else if (id == SURFACE_CAMERA) camera = &runner->surfaceCamera; + else if (id == GUI_CAMERA) camera = &runner->guiCamera; else return nullptr; if (!camera->allocated) return nullptr; return camera; @@ -1315,6 +1349,23 @@ static void initDefaultCameraFromRoomView(GMLCamera* camera, RoomView* roomView) camera->speedY = roomView->speedY; camera->objectId = roomView->objectId; camera->viewAngle = 0; + //make default projection + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) camera->viewWidth, -((float) camera->viewHeight), 32000.0, 0.0); + + Matrix4f ViewMatrix; + float x = camera->viewX + camera->viewWidth * 0.5f; + float y = camera->viewY + camera->viewHeight * 0.5f; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + Matrix4f_translate(&ViewMatrix, x, y, 0.0f); + Matrix4f_rotateZ(&ViewMatrix, -camera->viewAngle * (float) M_PI / 180.0f); + Matrix4f_translate(&ViewMatrix, -x, -y, 0.0f); + + + + camera->ProjectionMatrix = ProjectionMatrix; + camera->ViewMatrix = ViewMatrix; } // Copies the viewport (port) properties and enabled flag from parsed room data. diff --git a/src/runner.h b/src/runner.h index 1f5ce8b0..ede102c6 100644 --- a/src/runner.h +++ b/src/runner.h @@ -145,8 +145,8 @@ typedef struct { typedef struct { bool allocated; // slot in use (default cameras: set when the room enables the view; user cameras: camera_create/destroy) - int32_t viewX; - int32_t viewY; + float viewX; + float viewY; int32_t viewWidth; int32_t viewHeight; uint32_t borderX; @@ -155,9 +155,8 @@ typedef struct { int32_t speedY; int32_t objectId; // follow target (object index), -1 = none float viewAngle; - // Center derived from camera_set_view_mat; kept so set_view_mat / set_proj_mat (which arrive in either order) can both recompute the top-left viewX/viewY once the size from the proj matrix is known. - int32_t viewMatCenterX; - int32_t viewMatCenterY; + Matrix4f ViewMatrix; + Matrix4f ProjectionMatrix; } GMLCamera; typedef struct { @@ -473,6 +472,8 @@ struct Runner { RuntimeView views[MAX_VIEWS]; GMLCamera defaultCameras[MAX_DEFAULT_ROOM_CAMERAS]; GMLCamera userCameras[MAX_USER_CAMERAS]; + GMLCamera surfaceCamera; + GMLCamera guiCamera; RunnerGamepadState* gamepads; RuntimeBackground backgrounds[8]; uint32_t backgroundColor; // runtime-mutable (BGR format) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 723c4f18..b7727cb7 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -216,6 +216,27 @@ static DsStack* dsStackGet(Runner* runner, int32_t id) { return &runner->dsStackPool[id]; } +static void UpdateCameraViewSimple(GMLCamera* camera) { + + float x = camera->viewX + camera->viewWidth/2; + float y = camera->viewY + camera->viewHeight/2; + Matrix4f ViewMatrix; + Matrix4f_identity(&ViewMatrix); + Matrix4f_LookAt(&ViewMatrix, x, y, -16000.0, x, y, 16000.0, 0.0, 1.0, 0.0); + Matrix4f_translate(&ViewMatrix, x, y, 0.0f); + Matrix4f_rotateZ(&ViewMatrix, -camera->viewAngle * (float) M_PI / 180.0f); + Matrix4f_translate(&ViewMatrix, -x, -y, 0.0f); + + Matrix4f ProjectionMatrix; + Matrix4f_Orthographic(&ProjectionMatrix, (float) camera->viewWidth, -((float) camera->viewHeight), 32000.0, 0.0); + + + camera->ViewMatrix = ViewMatrix; + camera->ProjectionMatrix = ProjectionMatrix; + +} + + // ===[ BUILT-IN VARIABLE GET/SET ]=== static bool isValidAlarmIndex(int alarmIndex) { @@ -1551,22 +1572,34 @@ void VMBuiltins_setVariable(VMContext* ctx, Instance* inst, int16_t builtinVarId // View properties case BUILTIN_VAR_VIEW_XVIEW: { GMLCamera* camera = Runner_getCameraForView(runner, arrayIndex); - if (camera != nullptr) camera->viewX = RValue_toInt32(val); + if (camera != nullptr) { + camera->viewX = RValue_toReal(val); + UpdateCameraViewSimple(camera); + } return; } case BUILTIN_VAR_VIEW_YVIEW: { GMLCamera* camera = Runner_getCameraForView(runner, arrayIndex); - if (camera != nullptr) camera->viewY = RValue_toInt32(val); + if (camera != nullptr) { + camera->viewY = RValue_toInt32(val); + UpdateCameraViewSimple(camera); + } return; } case BUILTIN_VAR_VIEW_WVIEW: { GMLCamera* camera = Runner_getCameraForView(runner, arrayIndex); - if (camera != nullptr) camera->viewWidth = RValue_toInt32(val); + if (camera != nullptr) { + camera->viewWidth = RValue_toInt32(val); + UpdateCameraViewSimple(camera); + } return; } case BUILTIN_VAR_VIEW_HVIEW: { GMLCamera* camera = Runner_getCameraForView(runner, arrayIndex); - if (camera != nullptr) camera->viewHeight = RValue_toInt32(val); + if (camera != nullptr) { + camera->viewHeight = RValue_toInt32(val); + UpdateCameraViewSimple(camera); + } return; } case BUILTIN_VAR_VIEW_XPORT: @@ -1592,7 +1625,10 @@ void VMBuiltins_setVariable(VMContext* ctx, Instance* inst, int16_t builtinVarId return; case BUILTIN_VAR_VIEW_ANGLE: { GMLCamera* camera = Runner_getCameraForView(runner, arrayIndex); - if (camera != nullptr) camera->viewAngle = (float) RValue_toReal(val); + if (camera != nullptr) { + camera->viewAngle = (float) RValue_toReal(val); + UpdateCameraViewSimple(camera); + } return; } case BUILTIN_VAR_VIEW_HBORDER: { @@ -2847,14 +2883,7 @@ static RValue builtin_matrix_build_projection_ortho(MAYBE_UNUSED VMContext *ctx, if (toPrevMatrix && !rvalueIsMatrix(args[4])) return RValue_makeUndefined(); Matrix4f mat; - - memset(mat.m, 0, sizeof(mat.m)); - mat.m[Matrix_getIndex(0,0)] = 2.0f / width; - mat.m[Matrix_getIndex(1,1)] = 2.0f / height; - mat.m[Matrix_getIndex(2,2)] = 1.0f / (zfar - znear); - mat.m[Matrix_getIndex(3,3)] = 1.0f; - - mat.m[Matrix_getIndex(2,3)] = znear / (znear - zfar); + Matrix4f_Orthographic(&mat, width, height, zfar, znear); if (!toPrevMatrix) { return RValue_makeArray(matrixToGml(&mat)); @@ -2898,6 +2927,34 @@ static RValue builtin_matrix_build_projection_perspective_fov(MAYBE_UNUSED VMCon return RValue_makeArrayWeak(destArray); } } +static RValue builtin_matrix_get(MAYBE_UNUSED VMContext *ctx, RValue *args, int32_t argCount) { + int32_t Matrix = RValue_toInt32(args[0]); + if (Matrix < 0 || Matrix > 2) return RValue_makeUndefined(); + bool toPrevMatrix = argCount == 2; + GMLArray *destArray = toPrevMatrix ? args[1].array : nullptr; + if (toPrevMatrix && !rvalueIsMatrix(args[1])) return RValue_makeUndefined(); + + if (!toPrevMatrix) { + return RValue_makeArray(matrixToGml(&ctx->runner->renderer->gmlMatrices[Matrix])); + } else { + repeat (16, i) { + *GMLArray_slot(destArray, i) = RValue_makeReal(ctx->runner->renderer->gmlMatrices[Matrix].m[i]); + } + return RValue_makeArrayWeak(destArray); + } +} + +static RValue builtin_matrix_set(MAYBE_UNUSED VMContext *ctx, RValue *args, int32_t argCount) { + int32_t Matrix = RValue_toInt32(args[0]); + Matrix4f m; + matrixFromGml(&m, args[1].array); + if (Matrix < 0 || Matrix > 2) return RValue_makeUndefined(); + if (ctx->runner->renderer != nullptr) { + ctx->runner->renderer->vtable->setMatrix(ctx->runner->renderer, Matrix, m); + } + + return RValue_makeUndefined(); +} static RValue builtin_matrix_build_lookat(MAYBE_UNUSED VMContext *ctx, RValue *args, int32_t argCount) { if (argCount < 9 || argCount > 10) return RValue_makeUndefined(); @@ -2913,60 +2970,11 @@ static RValue builtin_matrix_build_lookat(MAYBE_UNUSED VMContext *ctx, RValue *a GMLReal xUp = RValue_toReal(args[6]); GMLReal yUp = RValue_toReal(args[7]); GMLReal zUp = RValue_toReal(args[8]); - GMLReal magUp = GMLReal_sqrt(xUp * xUp + yUp * yUp + zUp * zUp); - xUp /= magUp; - yUp /= magUp; - zUp /= magUp; - - GMLReal xLook = xTo - xFrom; - GMLReal yLook = yTo - yFrom; - GMLReal zLook = zTo - zFrom; - GMLReal magLook = GMLReal_sqrt(xLook * xLook + yLook * yLook + zLook * zLook); - xLook /= magLook; - yLook /= magLook; - zLook /= magLook; - - // normalised cross product between Up and Look - GMLReal xRight = yUp * zLook - zUp * yLook; - GMLReal yRight = zUp * xLook - xUp * zLook; - GMLReal zRight = xUp * yLook - yUp * xLook; - GMLReal magRight = GMLReal_sqrt(xRight * xRight + yRight * yRight + zRight * zRight); - xRight /= magRight; - yRight /= magRight; - zRight /= magRight; - - // normalised cross product between Look and Right - xUp = yLook * zRight - zLook * yRight; - yUp = zLook * xRight - xLook * zRight; - zUp = xLook * yRight - yLook * xRight; - magUp = GMLReal_sqrt(xUp * xUp + yUp * yUp + zUp * zUp); - xUp /= magUp; - yUp /= magUp; - zUp /= magUp; - - GMLReal x, y, z; - x = xFrom * xRight + yFrom * yRight + zFrom * zRight; - y = xFrom * xUp + yFrom * yUp + zFrom * zUp; - z = xFrom * xLook + yFrom * yLook + zFrom * zLook; Matrix4f matrix; Matrix4f_identity(&matrix); - matrix.m[Matrix_getIndex(0, 0)] = xRight; - matrix.m[Matrix_getIndex(0, 1)] = xUp; - matrix.m[Matrix_getIndex(0, 2)] = xLook; - - matrix.m[Matrix_getIndex(1, 0)] = yRight; - matrix.m[Matrix_getIndex(1, 1)] = yUp; - matrix.m[Matrix_getIndex(1, 2)] = yLook; - - matrix.m[Matrix_getIndex(2, 0)] = zRight; - matrix.m[Matrix_getIndex(2, 1)] = zUp; - matrix.m[Matrix_getIndex(2, 2)] = zLook; - - matrix.m[Matrix_getIndex(3, 0)] = -x; - matrix.m[Matrix_getIndex(3, 1)] = -y; - matrix.m[Matrix_getIndex(3, 2)] = -z; + Matrix4f_LookAt(&matrix, xFrom, yFrom, zFrom, xTo, yTo, zTo, xUp, yUp, zUp); bool toPrevMatrix = argCount == 10; GMLArray *destArray = toPrevMatrix ? args[9].array : nullptr; @@ -3469,8 +3477,9 @@ static RValue builtin_camera_set_view_pos(VMContext* ctx, RValue* args, int32_t Runner* runner = ctx->runner; GMLCamera* camera = Runner_getCameraById(runner, RValue_toInt32(args[0])); if (camera != nullptr) { - camera->viewX = RValue_toInt32(args[1]); - camera->viewY = RValue_toInt32(args[2]); + camera->viewX = RValue_toReal(args[1]); + camera->viewY = RValue_toReal(args[2]); + UpdateCameraViewSimple(camera); } return RValue_makeUndefined(); } @@ -3483,14 +3492,24 @@ static RValue builtin_camera_set_view_mat(VMContext* ctx, RValue* args, int32_t if (camera == nullptr || !rvalueIsMatrix(args[1])) return RValue_makeUndefined(); Matrix4f m; matrixFromGml(&m, args[1].array); - // For an axis-aligned 2D camera the view-matrix translation encodes -(camera center). - camera->viewMatCenterX = (int32_t) lround(-m.m[Matrix_getIndex(3, 0)]); - camera->viewMatCenterY = (int32_t) lround(-m.m[Matrix_getIndex(3, 1)]); - camera->viewX = camera->viewMatCenterX - camera->viewWidth / 2; - camera->viewY = camera->viewMatCenterY - camera->viewHeight / 2; + camera->ViewMatrix = m; return RValue_makeUndefined(); } +static RValue builtin_camera_get_view_mat(VMContext* ctx, RValue* args, int32_t argCount) { + Runner* runner = ctx->runner; + GMLCamera* camera = Runner_getCameraById(runner, RValue_toInt32(args[0])); + if (camera == nullptr) return RValue_makeUndefined(); + return RValue_makeArray(matrixToGml(&camera->ViewMatrix)); +} + +static RValue builtin_camera_get_proj_mat(VMContext* ctx, RValue* args, int32_t argCount) { + Runner* runner = ctx->runner; + GMLCamera* camera = Runner_getCameraById(runner, RValue_toInt32(args[0])); + if (camera == nullptr) return RValue_makeUndefined(); + return RValue_makeArray(matrixToGml(&camera->ProjectionMatrix)); +} + static RValue builtin_camera_set_proj_mat(VMContext* ctx, RValue* args, int32_t argCount) { if (2 > argCount) return RValue_makeUndefined(); Runner* runner = ctx->runner; @@ -3498,13 +3517,8 @@ static RValue builtin_camera_set_proj_mat(VMContext* ctx, RValue* args, int32_t if (camera == nullptr || !rvalueIsMatrix(args[1])) return RValue_makeUndefined(); Matrix4f m; matrixFromGml(&m, args[1].array); - // Orthographic projection: m[0,0] = 2/width, m[1,1] = 2/height. - GMLReal m00 = m.m[Matrix_getIndex(0, 0)]; - GMLReal m11 = m.m[Matrix_getIndex(1, 1)]; - if (m00 != 0.0) camera->viewWidth = (int32_t) lround(GMLReal_fabs(2.0 / m00)); - if (m11 != 0.0) camera->viewHeight = (int32_t) lround(GMLReal_fabs(2.0 / m11)); - camera->viewX = camera->viewMatCenterX - camera->viewWidth / 2; - camera->viewY = camera->viewMatCenterY - camera->viewHeight / 2; + camera->ProjectionMatrix = m; + camera->ProjectionMatrix.m[Matrix_getIndex(1, 1)] = -m.m[Matrix_getIndex(1, 1)]; return RValue_makeUndefined(); } @@ -3577,7 +3591,11 @@ static RValue builtin_camera_set_view_angle(VMContext* ctx, RValue* args, int32_ if (2 > argCount) return RValue_makeUndefined(); Runner* runner = ctx->runner; GMLCamera* camera = Runner_getCameraById(runner, RValue_toInt32(args[0])); - if (camera != nullptr) camera->viewAngle = (float) RValue_toReal(args[1]); + if (camera != nullptr) + { + camera->viewAngle = (float) RValue_toReal(args[1]); + UpdateCameraViewSimple(camera); + } return RValue_makeUndefined(); } @@ -3630,8 +3648,8 @@ static RValue builtin_camera_create_view(VMContext* ctx, RValue* args, int32_t a if (0 > id) return RValue_makeReal(-1); GMLCamera* camera = Runner_getCameraById(runner, id); // camera_create_view(room_x, room_y, room_w, room_h, [angle, object, x_speed, y_speed, x_border, y_border]) - if (argCount > 0) camera->viewX = RValue_toInt32(args[0]); - if (argCount > 1) camera->viewY = RValue_toInt32(args[1]); + if (argCount > 0) camera->viewX = RValue_toReal(args[0]); + if (argCount > 1) camera->viewY = RValue_toReal(args[1]); if (argCount > 2) camera->viewWidth = RValue_toInt32(args[2]); if (argCount > 3) camera->viewHeight = RValue_toInt32(args[3]); if (argCount > 4) camera->viewAngle = (float) RValue_toReal(args[4]); @@ -3640,6 +3658,10 @@ static RValue builtin_camera_create_view(VMContext* ctx, RValue* args, int32_t a if (argCount > 7) camera->speedY = RValue_toInt32(args[7]); if (argCount > 8) camera->borderX = (uint32_t) RValue_toInt32(args[8]); if (argCount > 9) camera->borderY = (uint32_t) RValue_toInt32(args[9]); + + + UpdateCameraViewSimple(camera); + return RValue_makeReal(id); } @@ -3674,7 +3696,7 @@ static RValue builtin_view_set_camera(VMContext* ctx, RValue* args, int32_t argC static RValue builtin_camera_get_active(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { Runner* runner = ctx->runner; if (runner->viewCurrent >= 0 && MAX_VIEWS > runner->viewCurrent) { - return RValue_makeReal(runner->views[runner->viewCurrent].cameraId); + return RValue_makeReal(runner->renderer->CameraCurrent); } return RValue_makeReal(-1); } @@ -3692,9 +3714,8 @@ static RValue builtin_camera_apply(VMContext* ctx, RValue* args, int32_t argCoun Runner* runner = ctx->runner; GMLCamera* camera = Runner_getCameraById(runner, RValue_toInt32(args[0])); if (camera != nullptr) { - Matrix4f worldToClip; - Matrix4f_viewProjection(&worldToClip, (float) camera->viewX, (float) camera->viewY, (float) camera->viewWidth, (float) camera->viewHeight, camera->viewAngle); - runner->renderer->vtable->applyProjection(runner->renderer, &worldToClip); + runner->renderer->vtable->applyProjection(runner->renderer, &camera->ViewMatrix, &camera->ProjectionMatrix); + runner->renderer->CameraCurrent = RValue_toInt32(args[0]); } return RValue_makeUndefined(); } @@ -15451,7 +15472,8 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "matrix_build_lookat", builtin_matrix_build_lookat); VM_registerBuiltin(ctx, "matrix_build_projection_ortho", builtin_matrix_build_projection_ortho); VM_registerBuiltin(ctx, "matrix_build_projection_perspective_fov", builtin_matrix_build_projection_perspective_fov); - + VM_registerBuiltin(ctx, "matrix_get", builtin_matrix_get); + VM_registerBuiltin(ctx, "matrix_set", builtin_matrix_set); // Random VM_registerBuiltin(ctx, "random", builtin_random); VM_registerBuiltin(ctx, "random_range", builtin_random_range); @@ -15494,7 +15516,9 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "camera_get_view_height", builtin_camera_get_view_height); VM_registerBuiltin(ctx, "camera_set_view_pos", builtin_camera_set_view_pos); VM_registerBuiltin(ctx, "camera_set_view_mat", builtin_camera_set_view_mat); + VM_registerBuiltin(ctx, "camera_get_view_mat", builtin_camera_get_view_mat); VM_registerBuiltin(ctx, "camera_set_proj_mat", builtin_camera_set_proj_mat); + VM_registerBuiltin(ctx, "camera_get_proj_mat", builtin_camera_get_proj_mat); VM_registerBuiltin(ctx, "camera_get_view_target", builtin_camera_get_view_target); VM_registerBuiltin(ctx, "camera_set_view_target", builtin_camera_set_view_target); VM_registerBuiltin(ctx, "camera_get_view_border_x", builtin_camera_get_view_border_x);