Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
278deb3
actually use the Camera's matricies and fix mistake with the matrix l…
Classic0306 Jun 11, 2026
fe6c642
remove ViewMatCenter
Classic0306 Jun 12, 2026
59bbbd5
fix tiny mistake
Classic0306 Jun 12, 2026
43143e6
change more things to use matricies! I kinda forgor what else I did
Classic0306 Jun 12, 2026
2a71137
I somehow already forgor what else I did
Classic0306 Jun 13, 2026
5f2d4f4
clean up a little bit
Classic0306 Jun 13, 2026
0161a05
make rendering to surfaces work in a hacky way
Classic0306 Jun 14, 2026
f5d5185
add and remove some comments
Classic0306 Jun 14, 2026
d550cd6
I think I've made spaghetti
Classic0306 Jun 14, 2026
96b1bcb
clean up a little bit
Classic0306 Jun 14, 2026
c4a20e8
remove hacks and hopefully it's good enough
Classic0306 Jun 15, 2026
10e0e92
remove more hacks and smth else I forgor
Classic0306 Jun 16, 2026
beabe39
rebase and fix little things
Classic0306 Jun 18, 2026
77fdb38
remove more thing, try fix GUI kinda
Classic0306 Jun 19, 2026
ce8c9aa
try to fix things, I wanna give up
Classic0306 Jun 19, 2026
ce3e26a
fix merge conflict
Classic0306 Jun 20, 2026
82468e7
fix little oversight and PS2 build
Classic0306 Jun 21, 2026
a1e440d
fix variable name casing in Matrix4f_LookAt
Classic0306 Jun 29, 2026
ec991b0
fix variable casing
Classic0306 Jun 29, 2026
e3bac07
am I done already...
Classic0306 Jun 29, 2026
885044c
fix merge conflicts
Classic0306 Jun 29, 2026
b61edad
did I finally do it correctly?
Classic0306 Jun 29, 2026
255a5e7
do the same changes to legacy renderer and fix little oversight
Classic0306 Jul 2, 2026
d72e3ea
hopefully fix PS2 build
Classic0306 Jul 3, 2026
6c88d9f
rebase what time now?
Classic0306 Jul 5, 2026
7e7eeed
maybe this would fix it?
Classic0306 Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 194 additions & 51 deletions src/gl/gl_renderer.c

Large diffs are not rendered by default.

200 changes: 147 additions & 53 deletions src/gl_legacy/gl_legacy_renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,52 @@ static bool hasFBO() {
// ===[ Helpers ]===

static void glApplyViewport(GLLegacyRenderer* gl, int32_t x, int32_t y, int32_t w, int32_t h) {
int32_t glY = gl->gameH - y - h;
glViewport(x, glY, w, h);
glViewport(x, y, w, h);
glEnable(GL_SCISSOR_TEST);
glScissor(x, glY, w, h);
glScissor(x, y, w, h);

gl->base.CPortX = x;
gl->base.CPortY = glY;
gl->base.CPortY = y;
gl->base.CPortW = w;
gl->base.CPortH = h;
}

// 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) {

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;

Matrix4f_flipClipY(&projection);

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(worldView.m);
}

// ===[ Vtable Implementations ]===

static void glInit(Renderer* renderer, DataWin* dataWin) {
GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer;
renderer->dataWin = dataWin;

Matrix4f world;
Matrix4f_identity(&world);
renderer->gmlMatrices[MATRIX_WORLD] = world;

if (!hasFBO()) {
fprintf(stderr, "GL: The legacy-gl renderer requires FBO support!\n");
abort();
Expand Down Expand Up @@ -174,7 +203,7 @@ static void glBeginFrame(Renderer* renderer, int32_t gameW, int32_t gameH, int32
glBindTexture(GL_TEXTURE_2D, 0);
}

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) {
GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer;

glBindTexture(GL_TEXTURE_2D, 0);
Expand All @@ -184,35 +213,23 @@ static void glBeginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_
// OpenGL viewport Y is bottom-up, game Y is top-down
glApplyViewport(gl, 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);

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glActiveTexture(GL_TEXTURE0);

renderer->previousViewMatrix = projection;
}

static void glEndView(MAYBE_UNUSED 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) {
Matrix4f projection = *worldToClip;
Matrix4f_flipClipY(&projection);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
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) {
GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer;

Expand All @@ -230,26 +247,68 @@ static void glBeginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t p
glApplyViewport(gl, portX, portY, portW, portH);
}

Matrix4f projection;
Matrix4f_guiProjection(&projection, (float) guiW, (float) guiH, (float) portW, (float) portH);
//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);

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glActiveTexture(GL_TEXTURE0);
}

static void glSetGuiProjection(MAYBE_UNUSED Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portW, int32_t portH, bool renderingToUserSurface) {
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.

renderer->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);
// Flip the projection when we are rendering to a user surface so it comes back upright.
if (renderingToUserSurface) Matrix4f_flipClipY(&projection);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
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(MAYBE_UNUSED Renderer* renderer) {
Expand Down Expand Up @@ -284,11 +343,10 @@ static void glClearScreen(MAYBE_UNUSED Renderer* renderer, uint32_t color, float
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);

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.
Expand Down Expand Up @@ -1549,34 +1607,70 @@ static void glLegacySurfaceFree(Renderer* renderer, int32_t surfaceId) {
static bool glLegacySetRenderTarget(Renderer* renderer, int32_t surfaceId, bool implicitApplicationSurface) {
GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer;

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;

glBindFramebuffer(GL_FRAMEBUFFER, gl->surfaces[surfaceId]);

if (surfaceId == renderer->runner->applicationSurfaceId && implicitApplicationSurface) {
glViewport(gl->base.CPortX, gl->base.CPortY, gl->base.CPortW, gl->base.CPortH);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(renderer->previousViewMatrix.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_SCISSOR_TEST);
glApplyProjection(renderer,&camera->viewMatrix,&camera->projectionMatrix);
return true;
}

int32_t w = gl->surfaceWidth[surfaceId];
int32_t h = gl->surfaceHeight[surfaceId];
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, w, h);
glViewport(0, 0, gl->surfaceWidth[surfaceId], gl->surfaceHeight[surfaceId]);
glDisable(GL_SCISSOR_TEST);

Matrix4f projection;
Matrix4f_identity(&projection);
Matrix4f_ortho(&projection, 0.0f, (float) w, 0.0f, (float) h, -1.0f, 1.0f);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.m);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return true;
}

Expand Down
70 changes: 70 additions & 0 deletions src/matrix_math.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,76 @@ static inline Matrix4f* Matrix4f_multiply(Matrix4f* dest, const Matrix4f* a, con
return dest;
}

static inline Matrix4f* Matrix4f_LookAt(Matrix4f* dest, float xFrom, float yFrom, float zFrom, float xTo, float yTo, float zTo, float xUp, float yUp, float zUp) {

float magUp = sqrt(xUp * xUp + yUp * yUp + zUp * zUp);
xUp /= magUp;
yUp /= magUp;
zUp /= magUp;

float xLook = xTo - xFrom;
float yLook = yTo - yFrom;
float zLook = zTo - zFrom;
float magLook = sqrt(xLook * xLook + yLook * yLook + zLook * zLook);
xLook /= magLook;
yLook /= magLook;
zLook /= magLook;

// normalised cross product between Up and Look
float xRight = yUp * zLook - zUp * yLook;
float yRight = zUp * xLook - xUp * zLook;
float zRight = xUp * yLook - yUp * xLook;
float 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;

float 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)
Expand Down
3 changes: 2 additions & 1 deletion src/ps2/gs_renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1157,8 +1157,9 @@ static void gsEndView(MAYBE_UNUSED Renderer* renderer) {
// No-op
}

static void gsApplyProjection(MAYBE_UNUSED Renderer* renderer, MAYBE_UNUSED const Matrix4f* worldToClip) {
static void gsApplyProjection(MAYBE_UNUSED Renderer* renderer, MAYBE_UNUSED const Matrix4f* viewMatrix, MAYBE_UNUSED const Matrix4f* projectionMatrix) {
// No-op
//Um but I do feel like the PS2 should be capable of this though?
}

static void gsSetGuiProjection(Renderer* renderer, int32_t guiW, int32_t guiH, MAYBE_UNUSED int32_t portW, MAYBE_UNUSED int32_t portH, MAYBE_UNUSED bool renderingToUserSurface) {
Expand Down
Loading
Loading