Dreamcast: 3D graphics and controller input support

3D graphics backend is temp using gldc
This commit is contained in:
UnknownShadow200 2023-08-12 11:22:07 +10:00
parent bfada15b23
commit 79b1a6720e
4 changed files with 473 additions and 18 deletions

432
src/Graphics_Dreamcast.c Normal file
View File

@ -0,0 +1,432 @@
#include "Core.h"
#if defined CC_BUILD_DREAMCAST
#include "_GraphicsBase.h"
#include "Errors.h"
#include "Logger.h"
#include "Window.h"
#include "GL/gl.h"
#include "GL/glkos.h"
#include "GL/glext.h"
#include <malloc.h>
#define PIXEL_FORMAT GL_RGBA
#define TRANSFER_FORMAT GL_UNSIGNED_BYTE
/* Current format and size of vertices */
static int gfx_stride, gfx_format = -1;
/*########################################################################################################################*
*---------------------------------------------------------General---------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_Create(void) {
glKosInit();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &Gfx.MaxTexWidth);
Gfx.MaxTexHeight = Gfx.MaxTexWidth;
Gfx.Created = true;
Gfx_RestoreState();
}
cc_bool Gfx_TryRestoreContext(void) {
return true;
}
void Gfx_Free(void) {
Gfx_FreeState();
}
#define gl_Toggle(cap) if (enabled) { glEnable(cap); } else { glDisable(cap); }
/*########################################################################################################################*
*-----------------------------------------------------State management----------------------------------------------------*
*#########################################################################################################################*/
static PackedCol gfx_clearColor;
void Gfx_SetFaceCulling(cc_bool enabled) { gl_Toggle(GL_CULL_FACE); }
void Gfx_SetAlphaBlending(cc_bool enabled) { gl_Toggle(GL_BLEND); }
void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
static void GL_ClearColor(PackedCol color) {
glClearColor(PackedCol_R(color) / 255.0f, PackedCol_G(color) / 255.0f,
PackedCol_B(color) / 255.0f, PackedCol_A(color) / 255.0f);
}
void Gfx_ClearCol(PackedCol color) {
if (color == gfx_clearColor) return;
GL_ClearColor(color);
gfx_clearColor = color;
}
void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
glColorMask(r, g, b, a);
}
void Gfx_SetDepthWrite(cc_bool enabled) { glDepthMask(enabled); }
void Gfx_SetDepthTest(cc_bool enabled) { gl_Toggle(GL_DEPTH_TEST); }
void Gfx_SetTexturing(cc_bool enabled) { }
void Gfx_SetAlphaTest(cc_bool enabled) { gl_Toggle(GL_ALPHA_TEST); }
void Gfx_DepthOnlyRendering(cc_bool depthOnly) {
cc_bool enabled = !depthOnly;
Gfx_SetColWriteMask(enabled, enabled, enabled, enabled);
gl_Toggle(GL_TEXTURE_2D);
}
/*########################################################################################################################*
*---------------------------------------------------------Matrices--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */
/* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */
*matrix = Matrix_Identity;
matrix->row1.X = 2.0f / width;
matrix->row2.Y = -2.0f / height;
matrix->row3.Z = -2.0f / (zFar - zNear);
matrix->row4.X = -1.0f;
matrix->row4.Y = 1.0f;
matrix->row4.Z = -(zFar + zNear) / (zFar - zNear);
}
static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
float zNear = 0.1f;
float c = (float)Cotangent(0.5f * fov);
/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */
/* For a FOV based perspective matrix, left/right/top/bottom are calculated as: */
/* left = -c * aspect, right = c * aspect, bottom = -c, top = c */
/* Calculations are simplified because of left/right and top/bottom symmetry */
*matrix = Matrix_Identity;
matrix->row1.X = c / aspect;
matrix->row2.Y = c;
matrix->row3.Z = -(zFar + zNear) / (zFar - zNear);
matrix->row3.W = -1.0f;
matrix->row4.Z = -(2.0f * zFar * zNear) / (zFar - zNear);
matrix->row4.W = 0.0f;
}
/*########################################################################################################################*
*-----------------------------------------------------------Misc----------------------------------------------------------*
*#########################################################################################################################*/
cc_result Gfx_TakeScreenshot(struct Stream* output) {
return ERR_NOT_SUPPORTED;
}
void Gfx_GetApiInfo(cc_string* info) {
int pointerSize = sizeof(void*) * 8;
String_Format1(info, "-- Using OpenGL (%i bit) --\n", &pointerSize);
String_Format1(info, "Vendor: %c\n", glGetString(GL_VENDOR));
String_Format1(info, "Renderer: %c\n", glGetString(GL_RENDERER));
String_Format1(info, "GL version: %c\n", glGetString(GL_VERSION));
String_Format2(info, "Max texture size: (%i, %i)\n", &Gfx.MaxTexWidth, &Gfx.MaxTexHeight);
}
void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
gfx_minFrameMs = minFrameMs;
gfx_vsync = vsync;
}
void Gfx_BeginFrame(void) { }
void Gfx_Clear(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void Gfx_EndFrame(void) {
glKosSwapBuffers();
if (gfx_minFrameMs) LimitFPS();
}
void Gfx_OnWindowResize(void) {
/* TODO: Eliminate this nasty hack.. */
Game_UpdateDimensions();
glViewport(0, 0, Game.Width, Game.Height);
}
/*########################################################################################################################*
*-------------------------------------------------------Index buffers-----------------------------------------------------*
*#########################################################################################################################*/
static cc_uint16 __attribute__((aligned(16))) gfx_indices[GFX_MAX_INDICES];
static void* gfx_vertices;
static int vb_size;
GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) {
fillFunc(gfx_indices, count, obj);
}
void Gfx_BindIb(GfxResourceID ib) { }
void Gfx_DeleteIb(GfxResourceID* ib) { }
/*########################################################################################################################*
*------------------------------------------------------Vertex buffers-----------------------------------------------------*
*#########################################################################################################################*/
GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) {
void* data = memalign(16, count * strideSizes[fmt]);
if (!data) Logger_Abort("Failed to allocate memory for GFX VB");
return data;
//return Mem_Alloc(count, strideSizes[fmt], "gfx VB");
}
void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; }
void Gfx_DeleteVb(GfxResourceID* vb) {
GfxResourceID data = *vb;
if (data) Mem_Free(data);
*vb = 0;
}
void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
vb_size = count * strideSizes[fmt];
return vb;
}
void Gfx_UnlockVb(GfxResourceID vb) {
gfx_vertices = vb;
//sceKernelDcacheWritebackInvalidateRange(vb, vb_size);
}
GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
void* data = memalign(16, maxVertices * strideSizes[fmt]);
if (!data) Logger_Abort("Failed to allocate memory for GFX VB");
return data;
//return Mem_Alloc(maxVertices, strideSizes[fmt], "gfx VB");
}
void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
vb_size = count * strideSizes[fmt];
return vb;
}
void Gfx_UnlockDynamicVb(GfxResourceID vb) {
gfx_vertices = vb;
//dcache_flush_range(vb, vb_size);
}
void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
gfx_vertices = vb;
Mem_Copy(vb, vertices, vCount * gfx_stride);
//dcache_flush_range(vertices, vCount * gfx_stride);
}
/*########################################################################################################################*
*---------------------------------------------------------Textures--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_BindTexture(GfxResourceID texId) {
int tex = texId;
glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
}
GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) {
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (!Math_IsPowOf2(bmp->width) || !Math_IsPowOf2(bmp->height)) {
Logger_Abort("Textures must have power of two dimensions");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp->width, bmp->height, 0, PIXEL_FORMAT, GL_UNSIGNED_BYTE, bmp->scan0);
return texId;
}
#define UPDATE_FAST_SIZE (64 * 64)
static CC_NOINLINE void UpdateTextureSlow(int x, int y, struct Bitmap* part, int rowWidth) {
BitmapCol buffer[UPDATE_FAST_SIZE];
void* ptr = (void*)buffer;
int count = part->width * part->height;
/* cannot allocate memory on the stack for very big updates */
if (count > UPDATE_FAST_SIZE) {
ptr = Mem_Alloc(count, 4, "Gfx_UpdateTexture temp");
}
CopyTextureData(ptr, part->width << 2, part, rowWidth << 2);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, GL_UNSIGNED_BYTE, ptr);
if (count > UPDATE_FAST_SIZE) Mem_Free(ptr);
}
void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
/* TODO: Use GL_UNPACK_ROW_LENGTH for Desktop OpenGL */
if (part->width == rowWidth) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, GL_UNSIGNED_BYTE, part->scan0);
} else {
UpdateTextureSlow(x, y, part, rowWidth);
}
}
void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) {
Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps);
}
void Gfx_DeleteTexture(GfxResourceID* texId) {
GLuint id = (GLuint)(*texId);
if (!id) return;
glDeleteTextures(1, &id);
*texId = 0;
}
void Gfx_EnableMipmaps(void) { }
void Gfx_DisableMipmaps(void) { }
/*########################################################################################################################*
*-----------------------------------------------------State management----------------------------------------------------*
*#########################################################################################################################*/
static PackedCol gfx_fogColor;
static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f;
static int gfx_fogMode = -1;
void Gfx_SetFog(cc_bool enabled) {
gfx_fogEnabled = enabled;
if (enabled) { glEnable(GL_FOG); } else { glDisable(GL_FOG); }
}
void Gfx_SetFogCol(PackedCol color) {
float rgba[4];
if (color == gfx_fogColor) return;
rgba[0] = PackedCol_R(color) / 255.0f;
rgba[1] = PackedCol_G(color) / 255.0f;
rgba[2] = PackedCol_B(color) / 255.0f;
rgba[3] = PackedCol_A(color) / 255.0f;
glFogfv(GL_FOG_COLOR, rgba);
gfx_fogColor = color;
}
void Gfx_SetFogDensity(float value) {
if (value == gfx_fogDensity) return;
glFogf(GL_FOG_DENSITY, value);
gfx_fogDensity = value;
}
void Gfx_SetFogEnd(float value) {
if (value == gfx_fogEnd) return;
glFogf(GL_FOG_END, value);
gfx_fogEnd = value;
}
void Gfx_SetFogMode(FogFunc func) {
static GLint modes[3] = { GL_LINEAR, GL_EXP, GL_EXP2 };
if (func == gfx_fogMode) return;
glFogi(GL_FOG_MODE, modes[func]);
gfx_fogMode = func;
}
/*########################################################################################################################*
*---------------------------------------------------------Matrices--------------------------------------------------------*
*#########################################################################################################################*/
static GLenum matrix_modes[] = { GL_PROJECTION, GL_MODELVIEW, GL_TEXTURE };
static int lastMatrix;
void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
glLoadMatrixf((const float*)matrix);
}
void Gfx_LoadIdentityMatrix(MatrixType type) {
if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
glLoadIdentity();
}
static struct Matrix texMatrix = Matrix_IdentityValue;
void Gfx_EnableTextureOffset(float x, float y) {
texMatrix.row4.X = x; texMatrix.row4.Y = y;
Gfx_LoadMatrix(2, &texMatrix);
}
void Gfx_DisableTextureOffset(void) { Gfx_LoadIdentityMatrix(2); }
/*########################################################################################################################*
*-------------------------------------------------------State setup-------------------------------------------------------*
*#########################################################################################################################*/
static void Gfx_FreeState(void) { FreeDefaultResources(); }
static void Gfx_RestoreState(void) {
InitDefaultResources();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
gfx_format = -1;
glHint(GL_FOG_HINT, GL_NICEST);
glAlphaFunc(GL_GREATER, 0.5f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
}
cc_bool Gfx_WarnIfNecessary(void) {
return false;
}
/*########################################################################################################################*
*----------------------------------------------------------Drawing--------------------------------------------------------*
*#########################################################################################################################*/
#define VB_PTR gfx_vertices
static void SetupVertices(int startVertex) {
if (gfx_format == VERTEX_FORMAT_TEXTURED) {
cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
} else {
cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED;
glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset));
glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset + 12));
}
}
void Gfx_SetVertexFormat(VertexFormat fmt) {
if (fmt == gfx_format) return;
gfx_format = fmt;
gfx_stride = strideSizes[fmt];
if (fmt == VERTEX_FORMAT_TEXTURED) {
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
} else {
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
}
void Gfx_DrawVb_Lines(int verticesCount) {
SetupVertices(0);
glDrawArrays(GL_LINES, 0, verticesCount);
}
void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
SetupVertices(startVertex);
glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, gfx_indices);
}
void Gfx_DrawVb_IndexedTris(int verticesCount) {
SetupVertices(0);
glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, gfx_indices);
}
void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, gfx_indices);
}
#endif

View File

@ -229,19 +229,21 @@ static void* ExecThread(void* param) {
}
void* Thread_Create(Thread_StartFunc func) {
// TODO: Probably wrong to use 0 here....
return NULL;//return thd_create(0, ExecThread, func);
kthread_attr_t attrs = { 0 };
attrs.stack_size = 64 * 1024;
attrs.label = "CC thread";
return thd_create_ex(&attrs, ExecThread, func);
}
void Thread_Start2(void* handle, Thread_StartFunc func) {
}
void Thread_Detach(void* handle) {
//thd_detach((kthread_t*)handle);
thd_detach((kthread_t*)handle);
}
void Thread_Join(void* handle) {
//thd_join((kthread_t*)handle, NULL);
thd_join((kthread_t*)handle, NULL);
}
void* Mutex_Create(void) {
@ -540,6 +542,10 @@ cc_bool DynamicLib_DescribeError(cc_string* dst) {
void Platform_Init(void) {
Platform_SingleProcess = true;
/*pspDebugSioInit();*/
char cwd[600] = { 0 };
char* ptr = getcwd(cwd, 600);
Platform_Log1("WORKING DIR: %c", ptr);
}
void Platform_Free(void) { }

View File

@ -138,7 +138,7 @@ void android_main(void) {
SetupProgram(0, NULL);
for (;;) { RunProgram(0, NULL); }
}
#elif defined CC_BUILD_3DS || defined CC_BUILD_PSP || defined CC_BUILD_GCWII
#elif defined CC_BUILD_3DS || defined CC_BUILD_PSP || defined CC_BUILD_GCWII || defined CC_BUILD_DREAMCAST
int main(int argc, char** argv) {
SetupProgram(argc, argv);
while (WindowInfo.Exists) {
@ -147,7 +147,7 @@ int main(int argc, char** argv) {
return 0;
}
#else
/* NOTE: main_real is used for when compiling with MingW without linking to startup files. */
/* NOTE: main_real is used for when compiling with MinGW without linking to startup files. */
/* Normally, the final code produced for "main" is our "main" combined with crt's main */
/* (mingw-w64-crt/crt/gccmain.c) - alas this immediately crashes the game on startup. */
/* Using main_real instead and setting main_real as the entrypoint fixes the crash. */

View File

@ -57,11 +57,17 @@ void Window_Close(void) {
/*########################################################################################################################*
*----------------------------------------------------Input processing-----------------------------------------------------*
*#########################################################################################################################*/
static void HandleButtons_Launcher(int mods) {
Input_SetNonRepeatable(CCPAD_START, mods & CONT_A);
Input_SetNonRepeatable(CCPAD_SELECT, mods & CONT_B);
Input_SetNonRepeatable(CCPAD_LEFT, mods & CONT_DPAD_LEFT);
Input_SetNonRepeatable(CCPAD_RIGHT, mods & CONT_DPAD_RIGHT);
Input_SetNonRepeatable(CCPAD_UP, mods & CONT_DPAD_UP);
Input_SetNonRepeatable(CCPAD_DOWN, mods & CONT_DPAD_DOWN);
}
static void HandleButtons_Game(int mods) {
// TODO: https://github.com/KallistiOS/KallistiOS/blob/90e09d81d7c1f9dc3f31290a8fff94e4d5ff304a/kernel/arch/dreamcast/include/dc/maple/controller.h#L41
//Input_SetNonRepeatable(CCPAD_L, mods & CONT_L);
//Input_SetNonRepeatable(CCPAD_R, mods & CONT_R);
// TODO CONT_Z, joysticks
// TODO CONT_Z
Input_SetNonRepeatable(CCPAD_A, mods & CONT_A);
Input_SetNonRepeatable(CCPAD_B, mods & CONT_B);
@ -76,14 +82,19 @@ static void HandleButtons_Game(int mods) {
Input_SetNonRepeatable(CCPAD_UP, mods & CONT_DPAD_UP);
Input_SetNonRepeatable(CCPAD_DOWN, mods & CONT_DPAD_DOWN);
}
static void HandleButtons_Launcher(int mods) {
Input_SetNonRepeatable(CCPAD_START, mods & CONT_A);
Input_SetNonRepeatable(CCPAD_SELECT, mods & CONT_B);
static void HandleController(cont_state_t* state) {
Input_SetNonRepeatable(CCPAD_L, state->ltrig > 10);
Input_SetNonRepeatable(CCPAD_R, state->rtrig > 10);
// TODO CONT_Z, joysticks
// TODO: verify values are write
Input_SetNonRepeatable(CCPAD_LEFT, mods & CONT_DPAD_LEFT);
Input_SetNonRepeatable(CCPAD_RIGHT, mods & CONT_DPAD_RIGHT);
Input_SetNonRepeatable(CCPAD_UP, mods & CONT_DPAD_UP);
Input_SetNonRepeatable(CCPAD_DOWN, mods & CONT_DPAD_DOWN);
if (Input.RawMode) {
int dx = state->joyx, dy = state->joyy;
if (Math_AbsI(dx) <= 8) dx = 0;
if (Math_AbsI(dy) <= 8) dy = 0;
Event_RaiseRawMove(&PointerEvents.RawMoved, dx / 8.0f, dy / 8.0f);
}
}
static void ProcessControllerInput(void) {
@ -100,7 +111,9 @@ static void ProcessControllerInput(void) {
HandleButtons_Launcher(mods);
} else {
HandleButtons_Game(mods);
HandleController(state);
}
}
void Window_ProcessEvents(double delta) {
@ -128,6 +141,10 @@ void Window_DrawFramebuffer(Rect2D r) {
// TODO: Don't redraw everything
int size = fb_bmp.width * fb_bmp.height * 4;
cc_uint32* row = Bitmap_GetRow(&fb_bmp, 20);
bfont_draw_str_ex(row, fb_bmp.width, 0xFFEEDDCC, 0x55667788, 32,
true, "ABC III LLL");
// TODO: double buffering ??
// https://dcemulation.org/phpBB/viewtopic.php?t=99999
// https://dcemulation.org/phpBB/viewtopic.php?t=43214