From e2ed4b2c2da5f096b5b45326ec2bcf2dce97788a Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Wed, 10 Jul 2024 19:13:32 +0300 Subject: [PATCH 01/27] glad: include debug and shader program extensions --- third-party/glad/include/glad/glad.h | 309 ++++++++++++++++++++++++++- third-party/glad/src/glad.c | 143 ++++++++++++- 2 files changed, 443 insertions(+), 9 deletions(-) diff --git a/third-party/glad/include/glad/glad.h b/third-party/glad/include/glad/glad.h index b8291f7..d040d4e 100644 --- a/third-party/glad/include/glad/glad.h +++ b/third-party/glad/include/glad/glad.h @@ -1,22 +1,23 @@ /* - OpenGL loader generated by glad 0.1.36 on Tue Jul 9 02:07:20 2024. + OpenGL loader generated by glad 0.1.36 on Wed Jul 10 12:56:38 2024. Language/Generator: C/C++ Specification: gl APIs: gl=1.5 Profile: compatibility Extensions: - + GL_ARB_shader_objects, + GL_KHR_debug Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="" + --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_shader_objects,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5 + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_shader_objects&extensions=GL_KHR_debug */ @@ -2278,6 +2279,306 @@ typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv #endif +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_BUFFER_KHR 0x82E0 +#define GL_SHADER_KHR 0x82E1 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_QUERY_KHR 0x82E3 +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_DISPLAY_LIST 0x82E7 +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +GLAPI int GLAD_GL_ARB_shader_objects; +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); +GLAPI PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB; +#define glDeleteObjectARB glad_glDeleteObjectARB +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC)(GLenum pname); +GLAPI PFNGLGETHANDLEARBPROC glad_glGetHandleARB; +#define glGetHandleARB glad_glGetHandleARB +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB; +#define glDetachObjectARB glad_glDetachObjectARB +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); +GLAPI PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB; +#define glCreateShaderObjectARB glad_glCreateShaderObjectARB +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB; +#define glShaderSourceARB glad_glShaderSourceARB +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); +GLAPI PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB; +#define glCompileShaderARB glad_glCompileShaderARB +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC)(void); +GLAPI PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB; +#define glCreateProgramObjectARB glad_glCreateProgramObjectARB +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); +GLAPI PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB; +#define glAttachObjectARB glad_glAttachObjectARB +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); +GLAPI PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB; +#define glLinkProgramARB glad_glLinkProgramARB +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); +GLAPI PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB; +#define glUseProgramObjectARB glad_glUseProgramObjectARB +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB programObj); +GLAPI PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB; +#define glValidateProgramARB glad_glValidateProgramARB +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC)(GLint location, GLfloat v0); +GLAPI PFNGLUNIFORM1FARBPROC glad_glUniform1fARB; +#define glUniform1fARB glad_glUniform1fARB +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC)(GLint location, GLfloat v0, GLfloat v1); +GLAPI PFNGLUNIFORM2FARBPROC glad_glUniform2fARB; +#define glUniform2fARB glad_glUniform2fARB +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI PFNGLUNIFORM3FARBPROC glad_glUniform3fARB; +#define glUniform3fARB glad_glUniform3fARB +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI PFNGLUNIFORM4FARBPROC glad_glUniform4fARB; +#define glUniform4fARB glad_glUniform4fARB +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC)(GLint location, GLint v0); +GLAPI PFNGLUNIFORM1IARBPROC glad_glUniform1iARB; +#define glUniform1iARB glad_glUniform1iARB +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC)(GLint location, GLint v0, GLint v1); +GLAPI PFNGLUNIFORM2IARBPROC glad_glUniform2iARB; +#define glUniform2iARB glad_glUniform2iARB +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2); +GLAPI PFNGLUNIFORM3IARBPROC glad_glUniform3iARB; +#define glUniform3iARB glad_glUniform3iARB +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI PFNGLUNIFORM4IARBPROC glad_glUniform4iARB; +#define glUniform4iARB glad_glUniform4iARB +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB; +#define glUniform1fvARB glad_glUniform1fvARB +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB; +#define glUniform2fvARB glad_glUniform2fvARB +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB; +#define glUniform3fvARB glad_glUniform3fvARB +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB; +#define glUniform4fvARB glad_glUniform4fvARB +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB; +#define glUniform1ivARB glad_glUniform1ivARB +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB; +#define glUniform2ivARB glad_glUniform2ivARB +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB; +#define glUniform3ivARB glad_glUniform3ivARB +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB; +#define glUniform4ivARB glad_glUniform4ivARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB; +#define glUniformMatrix2fvARB glad_glUniformMatrix2fvARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB; +#define glUniformMatrix3fvARB glad_glUniformMatrix3fvARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB; +#define glUniformMatrix4fvARB glad_glUniformMatrix4fvARB +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB; +#define glGetObjectParameterfvARB glad_glGetObjectParameterfvARB +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint *params); +GLAPI PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB; +#define glGetObjectParameterivARB glad_glGetObjectParameterivARB +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB; +#define glGetInfoLogARB glad_glGetInfoLogARB +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB; +#define glGetAttachedObjectsARB glad_glGetAttachedObjectsARB +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB *name); +GLAPI PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB; +#define glGetUniformLocationARB glad_glGetUniformLocationARB +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB; +#define glGetActiveUniformARB glad_glGetActiveUniformARB +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC)(GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB; +#define glGetUniformfvARB glad_glGetUniformfvARB +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC)(GLhandleARB programObj, GLint location, GLint *params); +GLAPI PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB; +#define glGetUniformivARB glad_glGetUniformivARB +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +GLAPI PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB; +#define glGetShaderSourceARB glad_glGetShaderSourceARB +#endif +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +GLAPI int GLAD_GL_KHR_debug; +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +#define glDebugMessageControl glad_glDebugMessageControl +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +#define glDebugMessageInsert glad_glDebugMessageInsert +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +#define glDebugMessageCallback glad_glDebugMessageCallback +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +#define glGetDebugMessageLog glad_glGetDebugMessageLog +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +#define glPushDebugGroup glad_glPushDebugGroup +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +#define glPopDebugGroup glad_glPopDebugGroup +typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel; +#define glObjectLabel glad_glObjectLabel +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +#define glGetObjectLabel glad_glGetObjectLabel +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +#define glObjectPtrLabel glad_glObjectPtrLabel +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +#define glGetObjectPtrLabel glad_glGetObjectPtrLabel +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params); +GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +#endif #ifdef __cplusplus } diff --git a/third-party/glad/src/glad.c b/third-party/glad/src/glad.c index bb050ff..0a07c64 100644 --- a/third-party/glad/src/glad.c +++ b/third-party/glad/src/glad.c @@ -1,22 +1,23 @@ /* - OpenGL loader generated by glad 0.1.36 on Tue Jul 9 02:07:20 2024. + OpenGL loader generated by glad 0.1.36 on Wed Jul 10 12:56:38 2024. Language/Generator: C/C++ Specification: gl APIs: gl=1.5 Profile: compatibility Extensions: - + GL_ARB_shader_objects, + GL_KHR_debug Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="" + --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_shader_objects,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5 + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_shader_objects&extensions=GL_KHR_debug */ #include @@ -713,6 +714,68 @@ PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; +int GLAD_GL_ARB_shader_objects = 0; +int GLAD_GL_KHR_debug = 0; +PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB = NULL; +PFNGLGETHANDLEARBPROC glad_glGetHandleARB = NULL; +PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB = NULL; +PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB = NULL; +PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB = NULL; +PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB = NULL; +PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB = NULL; +PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB = NULL; +PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB = NULL; +PFNGLUNIFORM1FARBPROC glad_glUniform1fARB = NULL; +PFNGLUNIFORM2FARBPROC glad_glUniform2fARB = NULL; +PFNGLUNIFORM3FARBPROC glad_glUniform3fARB = NULL; +PFNGLUNIFORM4FARBPROC glad_glUniform4fARB = NULL; +PFNGLUNIFORM1IARBPROC glad_glUniform1iARB = NULL; +PFNGLUNIFORM2IARBPROC glad_glUniform2iARB = NULL; +PFNGLUNIFORM3IARBPROC glad_glUniform3iARB = NULL; +PFNGLUNIFORM4IARBPROC glad_glUniform4iARB = NULL; +PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB = NULL; +PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB = NULL; +PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB = NULL; +PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB = NULL; +PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB = NULL; +PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB = NULL; +PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB = NULL; +PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB = NULL; +PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB = NULL; +PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB = NULL; +PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB = NULL; +PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB = NULL; +PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB = NULL; +PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB = NULL; +PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB = NULL; +PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB = NULL; +PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB = NULL; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; +PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); @@ -1183,9 +1246,77 @@ static void load_GL_VERSION_1_5(GLADloadproc load) { glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); } +static void load_GL_ARB_shader_objects(GLADloadproc load) { + if(!GLAD_GL_ARB_shader_objects) return; + glad_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)load("glDeleteObjectARB"); + glad_glGetHandleARB = (PFNGLGETHANDLEARBPROC)load("glGetHandleARB"); + glad_glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)load("glDetachObjectARB"); + glad_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)load("glCreateShaderObjectARB"); + glad_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)load("glShaderSourceARB"); + glad_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)load("glCompileShaderARB"); + glad_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)load("glCreateProgramObjectARB"); + glad_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)load("glAttachObjectARB"); + glad_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)load("glLinkProgramARB"); + glad_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)load("glUseProgramObjectARB"); + glad_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC)load("glValidateProgramARB"); + glad_glUniform1fARB = (PFNGLUNIFORM1FARBPROC)load("glUniform1fARB"); + glad_glUniform2fARB = (PFNGLUNIFORM2FARBPROC)load("glUniform2fARB"); + glad_glUniform3fARB = (PFNGLUNIFORM3FARBPROC)load("glUniform3fARB"); + glad_glUniform4fARB = (PFNGLUNIFORM4FARBPROC)load("glUniform4fARB"); + glad_glUniform1iARB = (PFNGLUNIFORM1IARBPROC)load("glUniform1iARB"); + glad_glUniform2iARB = (PFNGLUNIFORM2IARBPROC)load("glUniform2iARB"); + glad_glUniform3iARB = (PFNGLUNIFORM3IARBPROC)load("glUniform3iARB"); + glad_glUniform4iARB = (PFNGLUNIFORM4IARBPROC)load("glUniform4iARB"); + glad_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC)load("glUniform1fvARB"); + glad_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)load("glUniform2fvARB"); + glad_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)load("glUniform3fvARB"); + glad_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)load("glUniform4fvARB"); + glad_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC)load("glUniform1ivARB"); + glad_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC)load("glUniform2ivARB"); + glad_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC)load("glUniform3ivARB"); + glad_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC)load("glUniform4ivARB"); + glad_glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)load("glUniformMatrix2fvARB"); + glad_glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)load("glUniformMatrix3fvARB"); + glad_glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)load("glUniformMatrix4fvARB"); + glad_glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC)load("glGetObjectParameterfvARB"); + glad_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)load("glGetObjectParameterivARB"); + glad_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)load("glGetInfoLogARB"); + glad_glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)load("glGetAttachedObjectsARB"); + glad_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)load("glGetUniformLocationARB"); + glad_glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)load("glGetActiveUniformARB"); + glad_glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC)load("glGetUniformfvARB"); + glad_glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC)load("glGetUniformivARB"); + glad_glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC)load("glGetShaderSourceARB"); +} +static void load_GL_KHR_debug(GLADloadproc load) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert"); + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR"); + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR"); +} static int find_extensionsGL(void) { if (!get_exts()) return 0; - (void)&has_ext; + GLAD_GL_ARB_shader_objects = has_ext("GL_ARB_shader_objects"); + GLAD_GL_KHR_debug = has_ext("GL_KHR_debug"); free_exts(); return 1; } @@ -1252,6 +1383,8 @@ int gladLoadGLLoader(GLADloadproc load) { load_GL_VERSION_1_5(load); if (!find_extensionsGL()) return 0; + load_GL_ARB_shader_objects(load); + load_GL_KHR_debug(load); return GLVersion.major != 0 || GLVersion.minor != 0; } -- 2.30.2 From b280243d30c33026db1aa347612580c7990e20b9 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Wed, 10 Jul 2024 19:15:28 +0300 Subject: [PATCH 02/27] work-in-progress for 3d triangle rendering --- src/context.h | 5 +- src/game/scenes/ingame.c | 24 ++++- src/main.c | 27 ++++++ src/private/rendering.h | 23 +++++ src/rendering.c | 184 ++++++++++++++++++++++++++++++++++++--- src/rendering.h | 28 ++++++ src/textures.c | 11 +-- src/textures.h | 8 +- src/util.h | 13 +++ 9 files changed, 300 insertions(+), 23 deletions(-) diff --git a/src/context.h b/src/context.h index 7ad1d64..54c4f99 100644 --- a/src/context.h +++ b/src/context.h @@ -24,7 +24,10 @@ typedef struct context { struct rect_primitive *render_queue_rectangles; struct circle_primitive *render_queue_circles; - struct audio_channel_pair *audio_channels; + struct mesh_batch *uncolored_mesh_batches; /* texture_cache reflected */ + struct mesh_batch_item *uncolored_mesh_batches_loners; /* path reflected */ + + struct audio_channel_item *audio_channels; SDL_AudioDeviceID audio_device; int audio_stream_frequency; SDL_AudioFormat audio_stream_format; diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 1bea67b..9958539 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,7 +11,29 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - get_audio_args("soundtrack")->volume -= 0.01f; + // unfurl_triangle("/assets/title.png", + // (t_fvec3){ 1250, 700, 0 }, + // (t_fvec3){ 0, 800, 0 }, + // (t_fvec3){ 0, 0, 0 }, + // (t_shvec2){ 0, 360 }, + // (t_shvec2){ 0, 0 }, + // (t_shvec2){ 360, 0 }); + + unfurl_triangle("/assets/red.png", + (t_fvec3){ 0, 0, 0 }, + (t_fvec3){ RENDER_BASE_WIDTH, 0, 0 }, + (t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 }, + (t_shvec2){ 0, 0 }, + (t_shvec2){ 80, 0 }, + (t_shvec2){ 80, 80 }); + + unfurl_triangle("/assets/red.png", + (t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 }, + (t_fvec3){ 0, RENDER_BASE_HEIGHT, 0 }, + (t_fvec3){ 0, 0, 0 }, + (t_shvec2){ 80, 80 }, + (t_shvec2){ 0, 80 }, + (t_shvec2){ 0, 0 }); } diff --git a/src/main.c b/src/main.c index a41cb68..af5bfa2 100644 --- a/src/main.c +++ b/src/main.c @@ -44,6 +44,24 @@ static void poll_events(void) { } +static void APIENTRY opengl_log(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + (void)source; + (void)type; + (void)id; + (void)severity; + (void)userParam; + + log_info("OpenGL: %.*s\n", length, message); +} + + void main_loop(void) { /* if (!ctx.is_running) { @@ -149,6 +167,7 @@ static bool initialize(void) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); /* init got far enough to create a window */ ctx.window = SDL_CreateWindow("emerald", @@ -178,6 +197,8 @@ static bool initialize(void) { goto fail; } + log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); + /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); @@ -235,6 +256,12 @@ static bool initialize(void) { ctx.debug = false; #endif + /* hook up opengl debugging callback */ + if (ctx.debug) { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(opengl_log, NULL); + } + /* random seeding */ /* SDL_GetPerformanceCounter returns some platform-dependent number. */ /* it should vary between game instances. i checked! random enough for me. */ diff --git a/src/private/rendering.h b/src/private/rendering.h index 2c1dee4..fcaa614 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -5,6 +5,7 @@ #include "../util.h" #include +#include #include @@ -31,4 +32,26 @@ struct circle_primitive { t_fvec2 position; }; +/* batch of primitives with overlapping properties */ +struct mesh_batch { + GLuint buffer; /* server side storage */ + uint8_t *data; /* client side storage */ + // size_t buffer_len; /* element count */ +}; + +struct mesh_batch_item { + char *key; + struct mesh_batch value; +}; + +/* is structure that is in opengl vertex array */ +struct uncolored_space_triangle { + t_fvec3 v0; + t_shvec2 uv0; + t_fvec3 v1; + t_shvec2 uv1; + t_fvec3 v2; + t_shvec2 uv2; +}; + #endif diff --git a/src/rendering.c b/src/rendering.c index 734f679..7108981 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -17,6 +18,12 @@ void render_queue_clear(void) { arrsetlen(ctx.render_queue_sprites, 0); arrsetlen(ctx.render_queue_rectangles, 0); arrsetlen(ctx.render_queue_circles, 0); + + for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) + arrsetlen(ctx.uncolored_mesh_batches[i].data, 0); + + for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) + arrsetlen(ctx.uncolored_mesh_batches_loners[i].value.data, 0); } @@ -91,6 +98,78 @@ void push_circle(t_fvec2 position, float radius, t_color color) { } +/* TODO: automatic handling of repeating textures */ +void unfurl_triangle(const char *path, + t_fvec3 v0, + t_fvec3 v1, + t_fvec3 v2, + t_shvec2 uv0, + t_shvec2 uv1, + t_shvec2 uv2) +{ + /* corrected atlas texture coordinates */ + t_shvec2 uv0c, uv1c, uv2c; + struct mesh_batch *batch_p; + + textures_load(&ctx.texture_cache, path); + + const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path); + if (atlas_index == -1) { + /* loners span whole texture i assume */ + uv0c = uv0; + uv1c = uv1; + uv2c = uv2; + + batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value; + + } else { + const size_t old_len = arrlenu(ctx.uncolored_mesh_batches); + if ((size_t)atlas_index + 1 >= old_len) { + /* grow to accommodate texture cache atlases */ + arrsetlen(ctx.uncolored_mesh_batches, atlas_index + 1); + + /* zero initialize it all, it's a valid state */ + SDL_memset(&ctx.uncolored_mesh_batches[atlas_index], + 0, + sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len)); + } + + const SDL_Rect srcrect = ctx.texture_cache.hash[atlas_index].value.srcrect; /* TODO: does it work? */ + + /* fixed point galore */ + const int16_t srcratx = (int16_t)srcrect.x * (INT16_MAX / TEXTURE_ATLAS_SIZE); + const int16_t srcratw = (int16_t)srcrect.w * (INT16_MAX / TEXTURE_ATLAS_SIZE); + const int16_t srcrath = (int16_t)srcrect.h * (INT16_MAX / TEXTURE_ATLAS_SIZE); + const int16_t srcraty = (int16_t)srcrect.y * (INT16_MAX / TEXTURE_ATLAS_SIZE); /* flip? */ + + uv0c.x = (int16_t)(srcratx + ((uint16_t)((uv0.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); + uv0c.y = (int16_t)(srcraty + ((uint16_t)((uv0.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); + uv1c.x = (int16_t)(srcratx + ((uint16_t)((uv1.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); + uv1c.y = (int16_t)(srcraty + ((uint16_t)((uv1.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); + uv2c.x = (int16_t)(srcratx + ((uint16_t)((uv2.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); + uv2c.y = (int16_t)(srcraty + ((uint16_t)((uv2.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); + + batch_p = &ctx.uncolored_mesh_batches[atlas_index]; + } + + struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data; + struct uncolored_space_triangle pack = { + .v0 = v0, + // .uv0 = uv0c, + .uv0 = { 0, 0 }, + .v1 = v1, + // .uv1 = uv1c, + .uv1 = { INT16_MAX, 0 }, + .v2 = v2, + // .uv2 = uv2c, + .uv2 = { INT16_MAX, INT16_MAX }, + }; + arrpush(data, pack); + + batch_p->data = (uint8_t *)data; +} + + /* compare functions for the sort in render_sprites */ static int cmp_atlases(const void *a, const void *b) { int index_a = ((const struct sprite_primitive *)a)->atlas_index; @@ -312,9 +391,6 @@ static void render_circle(struct circle_primitive *circle) { static void render_sprites(void) { - if (ctx.texture_cache.is_dirty) - textures_update_current_atlas(&ctx.texture_cache); - sort_sprites(ctx.render_queue_sprites); for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) { @@ -337,36 +413,120 @@ static void render_circles(void) { } +static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) { + size_t data_len = arrlenu(batch->data); + + /* create vertex array object */ + if (batch->buffer == 0) + glGenBuffers(1, &batch->buffer); + + glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); + + /* TODO: try using mapped buffers while building batches instead? */ + /* this way we could skip client side copy that is kept until commitment */ + /* alternatively we could commit glBufferSubData based on a threshold */ + + /* upload batched data */ + glBufferData(GL_ARRAY_BUFFER, + data_len * sizeof (struct uncolored_space_triangle), + batch->data, + GL_STREAM_DRAW); + + /* vertex specification*/ + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, + GL_FLOAT, + offsetof(struct uncolored_space_triangle, v1), + (void *)offsetof(struct uncolored_space_triangle, v0)); + + /* note: propagates further to where texture binding is done */ + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, + GL_SHORT, + offsetof(struct uncolored_space_triangle, v1), + (void *)offsetof(struct uncolored_space_triangle, uv0)); + + // for (size_t i = 0; i < data_len; ++i) { + // struct uncolored_space_triangle t = ((struct uncolored_space_triangle *)batch->data)[i]; + // log_info("{%i, %i, %i, %i, %i, %i}\n", t.uv0.x, t.uv0.y, t.uv1.x, t.uv1.y, t.uv2.x, t.uv2.y); + // } + + /* commit for drawing */ + glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + /* invalidate the buffer immediately */ + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + + static void render_space(void) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); - glBegin(GL_TRIANGLES); - glColor4f(0.0, 1.0, 1.0, 1.0); - glVertex2f(300.0,210.0); - glVertex2f(340.0,215.0); - glVertex2f(320.0,250.0); - glEnd(); + glUseProgramObjectARB(0); + glActiveTexture(GL_TEXTURE0); + + /* solid white, no modulation */ + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) { + if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) { + SDL_Texture *const atlas = textures_get_atlas(&ctx.texture_cache, (int)i); + SDL_GL_BindTexture(atlas, NULL, NULL); + draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]); + SDL_GL_UnbindTexture(atlas); + } + } + + for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) { + if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) { + SDL_Texture *const atlas = textures_get_loner(&ctx.texture_cache, + ctx.uncolored_mesh_batches_loners[i].key); + SDL_GL_BindTexture(atlas, NULL, NULL); + draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value); + SDL_GL_UnbindTexture(atlas); + } + } glPopMatrix(); } void render(void) { + if (ctx.texture_cache.is_dirty) + textures_update_current_atlas(&ctx.texture_cache); + glClearColor((1.0f / 255) * 230, (1.0f / 255) * 230, (1.0f / 255) * 230, 1); - glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); - render_space(); + // glDisable(GL_CULL_FACE); + // glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + + /* TODO: write with no depth test, just fill it in */ render_sprites(); render_rectangles(); render_circles(); - // SDL_RenderPresent(ctx.renderer); + // glEnable(GL_CULL_FACE); + // glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + /* TODO: use depth test to optimize gui regions away */ + render_space(); + SDL_RenderFlush(ctx.renderer); SDL_GL_SwapWindow(ctx.window); } diff --git a/src/rendering.h b/src/rendering.h index 018f79f..99b0a4f 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -35,6 +35,34 @@ void push_rectangle(t_frect rect, t_color color); /* pushes a filled circle onto the circle render queue */ void push_circle(t_fvec2 position, float radius, t_color color); +/* pushes a textured 3d triangle onto the render queue */ +/* vertices are in absolute coordinates, relative to world origin */ +/* texture coordinates are in pixels */ +void unfurl_triangle(const char *path, + /* */ + t_fvec3 v0, + t_fvec3 v1, + t_fvec3 v2, + t_shvec2 uv0, + t_shvec2 uv1, + t_shvec2 uv2); + +/* TODO: */ +/* pushes a colored textured 3d triangle onto the render queue */ +// void unfurl_colored_triangle(const char *path, +// t_fvec3 v0, +// t_fvec3 v1, +// t_fvec3 v2, +// t_shvec2 uv0, +// t_shvec2 uv1, +// t_shvec2 uv2, +// t_color c0, +// t_color c1, +// t_color c2); + +/* TODO: billboarding */ +// http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 + /* renders the background, then the primitives in all render queues */ void render(void); diff --git a/src/textures.c b/src/textures.c index ef26380..c202ff2 100644 --- a/src/textures.c +++ b/src/textures.c @@ -15,7 +15,7 @@ #include -static SDL_Surface *image_to_surface(char *path) { +static SDL_Surface *image_to_surface(const char *path) { SDL_RWops *handle = PHYSFSRWOPS_openRead(path); if (handle == NULL) goto fail; @@ -236,7 +236,7 @@ void textures_dump_atlases(struct texture_cache *cache) { } -void textures_load(struct texture_cache *cache, char *path) { +void textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0) return; @@ -300,18 +300,19 @@ void textures_update_current_atlas(struct texture_cache *cache) { } -SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path) { +SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path) { struct texture_cache_item *texture = shgetp_null(cache->hash, path); if (texture == NULL) { CRY("Texture lookup failed.", "Tried to get texture that isn't loaded."); return (SDL_Rect){ 0, 0, 0, 0 }; } + return texture->value.srcrect; } -int textures_get_atlas_index(struct texture_cache *cache, char *path) { +int textures_get_atlas_index(struct texture_cache *cache, const char *path) { struct texture_cache_item *texture = shgetp_null(cache->hash, path); /* it might be a loner texture */ @@ -339,7 +340,7 @@ SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index) { } -SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path) { +SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path) { struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path); if (texture == NULL) { diff --git a/src/textures.h b/src/textures.h index bff3cbb..7d6c7ea 100644 --- a/src/textures.h +++ b/src/textures.h @@ -51,25 +51,25 @@ void textures_dump_atlases(struct texture_cache *cache); /* loads an image if it isn't in the cache, otherwise a no-op. */ /* can be called from anywhere at any time after init, useful if you want to */ /* preload textures you know will definitely be used */ -void textures_load(struct texture_cache *cache, char *path); +void textures_load(struct texture_cache *cache, const char *path); /* repacks the current texture atlas based on the texture cache */ void textures_update_current_atlas(struct texture_cache *cache); /* returns a rect in a texture cache atlas based on a path, for drawing */ /* if the texture is not found, returns a zero-filled rect (so check w or h) */ -SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path); +SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path); /* returns which atlas the texture in the path is in, starting from 0 */ /* if the texture is not found, returns INT_MIN */ -int textures_get_atlas_index(struct texture_cache *cache, char *path); +int textures_get_atlas_index(struct texture_cache *cache, const char *path); /* returns a pointer to the atlas at `index` */ /* if the index is out of bounds, returns NULL. */ /* you can get the index via texture_get_atlas_index */ SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index); -SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path); +SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path); /* returns the number of atlases in the cache */ size_t textures_get_num_atlases(struct texture_cache *cache); diff --git a/src/util.h b/src/util.h index bb90b3e..34c69df 100644 --- a/src/util.h +++ b/src/util.h @@ -109,6 +109,19 @@ typedef struct fvec2 { } t_fvec2; +/* a point in some three dimension space (floating point) */ +/* y goes up, x goes to the right */ +typedef struct fvec3 { + float x, y, z; +} t_fvec3; + + +/* a point in some space (short) */ +typedef struct shvec2 { + short x, y; +} t_shvec2; + + /* decrements an lvalue (which should be an int), stopping at 0 */ /* meant for tick-based timers in game logic */ /* -- 2.30.2 From 34cf504b2b1d18e6ea54d73af1c3fbc27426a998 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Wed, 10 Jul 2024 19:16:39 +0300 Subject: [PATCH 03/27] rename *_pair to *_item, as it's the convention used before --- src/audio.c | 6 +++--- src/private/audio.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audio.c b/src/audio.c index c836f3b..7a28241 100644 --- a/src/audio.c +++ b/src/audio.c @@ -57,7 +57,7 @@ static int64_t get_audio_data(const char *path, unsigned char **data) { void play_audio(const char *path, const char *channel) { - const struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + const struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); if (!pair) play_audio_ex(path, channel, get_default_audio_args()); else @@ -166,7 +166,7 @@ static void repeat_audio(struct audio_channel *channel) { void play_audio_ex(const char *path, const char *channel, t_play_audio_args args) { - struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); /* create a channel if it doesn't exist */ if (!pair) { @@ -191,7 +191,7 @@ void play_audio_ex(const char *path, const char *channel, t_play_audio_args args t_play_audio_args *get_audio_args(const char *channel) { - struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); if (!pair) return NULL; diff --git a/src/private/audio.h b/src/private/audio.h index d94589b..696cc76 100644 --- a/src/private/audio.h +++ b/src/private/audio.h @@ -44,7 +44,7 @@ struct audio_channel { }; -struct audio_channel_pair { +struct audio_channel_item { char *key; struct audio_channel value; }; -- 2.30.2 From 55d85399e9c38f2761c76d2fa0fa423d3240687f Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Fri, 12 Jul 2024 21:10:25 +0300 Subject: [PATCH 04/27] full removal of SDL_Renderer usage, working spatial triangle rendering, temporary(?) regression of HDPI --- src/config.h | 1 + src/context.h | 1 - src/game/scenes/ingame.c | 32 ++--- src/game_api.h | 1 + src/input.c | 22 ++-- src/input.h | 2 +- src/main.c | 19 +-- src/private/rendering.h | 6 +- src/private/textures.h | 41 ++++++ src/rendering.c | 278 ++++++++++++++++++++++++--------------- src/textures.c | 120 +++++++++++++---- src/textures.h | 49 +------ 12 files changed, 348 insertions(+), 224 deletions(-) create mode 100644 src/private/textures.h diff --git a/src/config.h b/src/config.h index 805214f..11d647f 100644 --- a/src/config.h +++ b/src/config.h @@ -17,6 +17,7 @@ #define RENDER_BASE_WIDTH 640 #define RENDER_BASE_HEIGHT 360 +#define RENDER_BASE_RATIO ((float)RENDER_BASE_WIDTH / (float)RENDER_BASE_HEIGHT) #define TEXTURE_ATLAS_SIZE 2048 #define TEXTURE_ATLAS_BIT_DEPTH 32 diff --git a/src/context.h b/src/context.h index 54c4f99..bf31e21 100644 --- a/src/context.h +++ b/src/context.h @@ -54,7 +54,6 @@ typedef struct context { unsigned int update_multiplicity; SDL_GLContext *gl_context; - SDL_Renderer *renderer; SDL_Window *window; uint32_t window_id; int window_w; diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 9958539..66e36f9 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,28 +11,20 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - // unfurl_triangle("/assets/title.png", - // (t_fvec3){ 1250, 700, 0 }, - // (t_fvec3){ 0, 800, 0 }, - // (t_fvec3){ 0, 0, 0 }, - // (t_shvec2){ 0, 360 }, - // (t_shvec2){ 0, 0 }, - // (t_shvec2){ 360, 0 }); - - unfurl_triangle("/assets/red.png", - (t_fvec3){ 0, 0, 0 }, - (t_fvec3){ RENDER_BASE_WIDTH, 0, 0 }, - (t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 }, + unfurl_triangle("/assets/player/baron-walk.png", + (t_fvec3){ -1, -1, 0 }, + (t_fvec3){ 1, -1, 0 }, + (t_fvec3){ 1, 1, 0 }, (t_shvec2){ 0, 0 }, - (t_shvec2){ 80, 0 }, - (t_shvec2){ 80, 80 }); + (t_shvec2){ 48, 0 }, + (t_shvec2){ 48, 48 }); - unfurl_triangle("/assets/red.png", - (t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 }, - (t_fvec3){ 0, RENDER_BASE_HEIGHT, 0 }, - (t_fvec3){ 0, 0, 0 }, - (t_shvec2){ 80, 80 }, - (t_shvec2){ 0, 80 }, + unfurl_triangle("/assets/player/baron-walk.png", + (t_fvec3){ 1, 1, 0 }, + (t_fvec3){ -1, 1, 0 }, + (t_fvec3){ -1, -1, 0 }, + (t_shvec2){ 48, 48 }, + (t_shvec2){ 0, 48 }, (t_shvec2){ 0, 0 }); } diff --git a/src/game_api.h b/src/game_api.h index 79efee9..49760d2 100644 --- a/src/game_api.h +++ b/src/game_api.h @@ -5,6 +5,7 @@ #include "context.h" #include "rendering.h" +#include "audio.h" #include "util.h" #include diff --git a/src/input.c b/src/input.c index 0117801..ad9196e 100644 --- a/src/input.c +++ b/src/input.c @@ -36,18 +36,21 @@ static void update_action_pressed_state(struct input_state *input, struct action else { action->just_changed = !action->is_pressed; action->is_pressed = true; + action->position.x = (float)input->mouse_window_position.x; + action->position.x = (float)input->mouse_window_position.x; + /* TODO: */ /* * SDL_RenderWindowToLogical will turn window mouse * coords into a position inside the logical render * area. this has to be done to get an accurate point * that can actually be used in game logic */ - SDL_RenderWindowToLogical(input->renderer, - input->mouse_window_position.x, - input->mouse_window_position.y, - &action->position.x, - &action->position.y); + // SDL_RenderWindowToLogical(input->renderer, + // input->mouse_window_position.x, + // input->mouse_window_position.y, + // &action->position.x, + // &action->position.y); return; } break; @@ -59,9 +62,9 @@ static void update_action_pressed_state(struct input_state *input, struct action static void input_bind_code_to_action(struct input_state *input, - char *action_name, - enum button_source source, - union button_code code) + char *action_name, + enum button_source source, + union button_code code) { struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); if (action_item == NULL) { @@ -172,9 +175,8 @@ static void input_unbind_code_from_action(struct input_state *input, } -void input_state_init(struct input_state *input, SDL_Renderer *renderer) { +void input_state_init(struct input_state *input) { sh_new_strdup(input->action_hash); - input->renderer = renderer; } diff --git a/src/input.h b/src/input.h index fab59b3..f514342 100644 --- a/src/input.h +++ b/src/input.h @@ -63,7 +63,7 @@ struct input_state { }; -void input_state_init(struct input_state *input, SDL_Renderer *renderer); +void input_state_init(struct input_state *input); void input_state_deinit(struct input_state *input); void input_state_update(struct input_state *input); diff --git a/src/main.c b/src/main.c index af5bfa2..38db7f4 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,8 @@ static void poll_events(void) { switch (e.window.event) { case SDL_WINDOWEVENT_RESIZED: + ctx.window_w = e.window.data1; + ctx.window_h = e.window.data2; break; } @@ -175,7 +177,7 @@ static bool initialize(void) { SDL_WINDOWPOS_CENTERED, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, - SDL_WINDOW_ALLOW_HIGHDPI | + // SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); if (ctx.window == NULL) { @@ -201,14 +203,13 @@ static bool initialize(void) { /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); - - /* now that we have a window, we know a renderer can be created */ - ctx.renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_PRESENTVSYNC); - /* SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "overscan"); */ - /* SDL_RenderSetIntegerScale(ctx.renderer, SDL_TRUE); */ - SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); - SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); + glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); + + /* TODO: */ + // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); + ctx.window_w = RENDER_BASE_WIDTH; + ctx.window_h = RENDER_BASE_HEIGHT; /* audio initialization */ { @@ -298,7 +299,7 @@ static bool initialize(void) { } /* input */ - input_state_init(&ctx.input, ctx.renderer); + input_state_init(&ctx.input); /* scripting */ /* diff --git a/src/private/rendering.h b/src/private/rendering.h index fcaa614..75b662f 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -47,11 +47,11 @@ struct mesh_batch_item { /* is structure that is in opengl vertex array */ struct uncolored_space_triangle { t_fvec3 v0; - t_shvec2 uv0; + t_fvec2 uv0; t_fvec3 v1; - t_shvec2 uv1; + t_fvec2 uv1; t_fvec3 v2; - t_shvec2 uv2; + t_fvec2 uv2; }; #endif diff --git a/src/private/textures.h b/src/private/textures.h new file mode 100644 index 0000000..87b49d4 --- /dev/null +++ b/src/private/textures.h @@ -0,0 +1,41 @@ +#ifndef PRIVATE_TEXTURES_H +#define PRIVATE_TEXTURES_H + +#include +#include +#include + +#include + +struct texture { + SDL_Rect srcrect; /* position in atlas */ + SDL_Surface *data; /* original image data */ + GLuint loner_data; /* loner textures store their data directly */ + int atlas_index; /* which atlas the texture is in */ + int8_t layer; +}; + + +struct texture_cache_item { + char *key; + struct texture value; +}; + + +struct texture_cache { + /* from context */ + SDL_Window *window; + + struct texture_cache_item *hash; + struct texture_cache_item *loner_hash; + + stbrp_node *node_buffer; /* used internally by stb_rect_pack */ + + SDL_Surface **atlas_surfaces; + GLuint *atlas_textures; + int atlas_index; + + bool is_dirty; /* current atlas needs to be recreated */ +}; + +#endif diff --git a/src/rendering.c b/src/rendering.c index 7108981..508835a 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -10,6 +10,7 @@ #include #include +/* http://www.swiftless.com/opengltuts.html */ void render_queue_clear(void) { /* since i don't intend to free the queues, */ @@ -108,17 +109,20 @@ void unfurl_triangle(const char *path, t_shvec2 uv2) { /* corrected atlas texture coordinates */ - t_shvec2 uv0c, uv1c, uv2c; + t_fvec2 uv0c, uv1c, uv2c; struct mesh_batch *batch_p; textures_load(&ctx.texture_cache, path); + const SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, path); const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path); if (atlas_index == -1) { - /* loners span whole texture i assume */ - uv0c = uv0; - uv1c = uv1; - uv2c = uv2; + uv0c.x = (float)uv0.x / (float)srcrect.w; + uv0c.y = (float)uv0.y / (float)srcrect.h; + uv1c.x = (float)uv1.x / (float)srcrect.w; + uv1c.y = (float)uv1.y / (float)srcrect.h; + uv2c.x = (float)uv2.x / (float)srcrect.w; + uv2c.y = (float)uv2.y / (float)srcrect.h; batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value; @@ -134,20 +138,17 @@ void unfurl_triangle(const char *path, sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len)); } - const SDL_Rect srcrect = ctx.texture_cache.hash[atlas_index].value.srcrect; /* TODO: does it work? */ + const float wr = (float)srcrect.w / TEXTURE_ATLAS_SIZE; + const float hr = (float)srcrect.h / TEXTURE_ATLAS_SIZE; + const float xr = (float)srcrect.x / TEXTURE_ATLAS_SIZE; + const float yr = (float)srcrect.y / TEXTURE_ATLAS_SIZE; - /* fixed point galore */ - const int16_t srcratx = (int16_t)srcrect.x * (INT16_MAX / TEXTURE_ATLAS_SIZE); - const int16_t srcratw = (int16_t)srcrect.w * (INT16_MAX / TEXTURE_ATLAS_SIZE); - const int16_t srcrath = (int16_t)srcrect.h * (INT16_MAX / TEXTURE_ATLAS_SIZE); - const int16_t srcraty = (int16_t)srcrect.y * (INT16_MAX / TEXTURE_ATLAS_SIZE); /* flip? */ - - uv0c.x = (int16_t)(srcratx + ((uint16_t)((uv0.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); - uv0c.y = (int16_t)(srcraty + ((uint16_t)((uv0.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); - uv1c.x = (int16_t)(srcratx + ((uint16_t)((uv1.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); - uv1c.y = (int16_t)(srcraty + ((uint16_t)((uv1.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); - uv2c.x = (int16_t)(srcratx + ((uint16_t)((uv2.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7)); - uv2c.y = (int16_t)(srcraty + ((uint16_t)((uv2.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7)); + uv0c.x = xr + ((float)uv0.x / (float)srcrect.w) * wr; + uv0c.y = yr + ((float)uv0.y / (float)srcrect.h) * hr; + uv1c.x = xr + ((float)uv1.x / (float)srcrect.w) * wr; + uv1c.y = yr + ((float)uv1.y / (float)srcrect.h) * hr; + uv2c.x = xr + ((float)uv2.x / (float)srcrect.w) * wr; + uv2c.y = yr + ((float)uv2.y / (float)srcrect.h) * hr; batch_p = &ctx.uncolored_mesh_batches[atlas_index]; } @@ -155,14 +156,11 @@ void unfurl_triangle(const char *path, struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data; struct uncolored_space_triangle pack = { .v0 = v0, - // .uv0 = uv0c, - .uv0 = { 0, 0 }, + .uv0 = uv0c, .v1 = v1, - // .uv1 = uv1c, - .uv1 = { INT16_MAX, 0 }, + .uv1 = uv1c, .v2 = v2, - // .uv2 = uv2c, - .uv2 = { INT16_MAX, INT16_MAX }, + .uv2 = uv2c, }; arrpush(data, pack); @@ -229,6 +227,7 @@ static int cmp_layers(const void *a, const void *b) { } +/* TODO: not that we're SDL free we need to implement batching ourselves */ /* necessary to allow the renderer to draw in batches in the best case */ static void sort_sprites(struct sprite_primitive *sprites) { size_t sprites_len = arrlenu(sprites); @@ -239,66 +238,86 @@ static void sort_sprites(struct sprite_primitive *sprites) { } +static void upload_quad_vertices(t_frect rect) { + /* client memory needs to be reachable on glDraw*, so */ + static float vertices[6 * 2]; + + vertices[0] = rect.x; vertices[1] = rect.y; + vertices[2] = rect.x; vertices[3] = rect.y + rect.h; + vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h; + vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h; + vertices[8] = rect.x + rect.w; vertices[9] = rect.y; + vertices[10] = rect.x; vertices[11] = rect.y; + + glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices); +} + + +/* TODO: texture flipping */ +/* assumes that orthogonal matrix setup is done already */ static void render_sprite(struct sprite_primitive *sprite) { - SDL_Rect srcrect_value = { 0 }; - SDL_Rect *srcrect = &srcrect_value; - SDL_Texture *texture = NULL; + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); /* loner */ if (sprite->atlas_index == -1) { - srcrect = NULL; - texture = textures_get_loner(&ctx.texture_cache, sprite->path); + textures_bind_loner(&ctx.texture_cache, sprite->path, GL_TEXTURE_2D); + glTexCoordPointer(2, + GL_SHORT, + 0, + (void *)(int16_t[6 * 2]) { + 0, INT16_MAX, + 0, 0, + INT16_MAX, 0, + INT16_MAX, 0, + INT16_MAX, INT16_MAX, + 0, INT16_MAX }); } else { - *srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path); - texture = textures_get_atlas(&ctx.texture_cache, sprite->atlas_index); + SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path); + textures_bind_atlas(&ctx.texture_cache, sprite->atlas_index, GL_TEXTURE_2D); + glTexCoordPointer(2, + GL_FLOAT, + 0, + /* TODO: try using shorts */ + (void *)(float[6 * 2]) { + (float)srcrect.x / TEXTURE_ATLAS_SIZE, + (float)srcrect.y / TEXTURE_ATLAS_SIZE, + (float)srcrect.x / TEXTURE_ATLAS_SIZE, + (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, + (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, + (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, + (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, + (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, + (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, + (float)srcrect.y / TEXTURE_ATLAS_SIZE, + (float)srcrect.x / TEXTURE_ATLAS_SIZE, + (float)srcrect.y / TEXTURE_ATLAS_SIZE }); } - SDL_RendererFlip flip = SDL_FLIP_NONE; - if (sprite->flip_x) - flip |= SDL_FLIP_HORIZONTAL; - if (sprite->flip_y) - flip |= SDL_FLIP_VERTICAL; + glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a); - SDL_SetTextureColorMod(texture, - sprite->color.r, - sprite->color.g, - sprite->color.b); - SDL_SetTextureAlphaMod(texture, sprite->color.a); - SDL_SetTextureBlendMode(texture, sprite->blend_mode); + // SDL_SetTextureBlendMode(texture, sprite->blend_mode); - SDL_Rect rect = { - (int)sprite->rect.x, - (int)sprite->rect.y, - (int)sprite->rect.w, - (int)sprite->rect.h - }; + glEnableClientState(GL_VERTEX_ARRAY); + upload_quad_vertices(sprite->rect); - SDL_RenderCopyEx(ctx.renderer, - texture, - srcrect, - &rect, - sprite->rotation, - NULL, - flip); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glBindTexture(GL_TEXTURE_2D, 0); } static void render_rectangle(struct rect_primitive *rectangle) { - SDL_SetRenderDrawColor(ctx.renderer, - rectangle->color.r, - rectangle->color.g, - rectangle->color.b, - rectangle->color.a); + glColor4ub(rectangle->color.r, rectangle->color.g, + rectangle->color.b, rectangle->color.a); - SDL_Rect rect = { - (int)rectangle->rect.x, - (int)rectangle->rect.y, - (int)rectangle->rect.w, - (int)rectangle->rect.h - }; + glEnableClientState(GL_VERTEX_ARRAY); + upload_quad_vertices(rectangle->rect); - SDL_RenderFillRect(ctx.renderer, &rect); - SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255); + glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableClientState(GL_VERTEX_ARRAY); } @@ -379,11 +398,25 @@ static void render_circle(struct circle_primitive *circle) { &vertices, &indices); - SDL_RenderGeometry(ctx.renderer, NULL, - vertices, - num_vertices + 1, /* vertices + center vertex */ - indices, - num_vertices * 3); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, + GL_FLOAT, + sizeof (SDL_Vertex), + &vertices->position); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + sizeof (SDL_Vertex), + &vertices->color); + + glDrawElements(GL_TRIANGLES, + num_vertices * 3, + GL_UNSIGNED_INT, + indices); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); free(vertices); free(indices); @@ -439,19 +472,13 @@ static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) { offsetof(struct uncolored_space_triangle, v1), (void *)offsetof(struct uncolored_space_triangle, v0)); - /* note: propagates further to where texture binding is done */ glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, - GL_SHORT, + GL_FLOAT, offsetof(struct uncolored_space_triangle, v1), (void *)offsetof(struct uncolored_space_triangle, uv0)); - // for (size_t i = 0; i < data_len; ++i) { - // struct uncolored_space_triangle t = ((struct uncolored_space_triangle *)batch->data)[i]; - // log_info("{%i, %i, %i, %i, %i, %i}\n", t.uv0.x, t.uv0.y, t.uv1.x, t.uv1.y, t.uv2.x, t.uv2.y); - // } - /* commit for drawing */ glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len); @@ -465,12 +492,7 @@ static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) { static void render_space(void) { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); - - glUseProgramObjectARB(0); + glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); /* solid white, no modulation */ @@ -478,24 +500,20 @@ static void render_space(void) { for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) { if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) { - SDL_Texture *const atlas = textures_get_atlas(&ctx.texture_cache, (int)i); - SDL_GL_BindTexture(atlas, NULL, NULL); + textures_bind_atlas(&ctx.texture_cache, (int)i, GL_TEXTURE_2D); draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]); - SDL_GL_UnbindTexture(atlas); } } for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) { if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) { - SDL_Texture *const atlas = textures_get_loner(&ctx.texture_cache, - ctx.uncolored_mesh_batches_loners[i].key); - SDL_GL_BindTexture(atlas, NULL, NULL); + textures_bind_loner(&ctx.texture_cache, + ctx.uncolored_mesh_batches_loners[i].key, + GL_TEXTURE_2D); + draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value); - SDL_GL_UnbindTexture(atlas); } } - - glPopMatrix(); } @@ -503,6 +521,30 @@ void render(void) { if (ctx.texture_cache.is_dirty) textures_update_current_atlas(&ctx.texture_cache); + /* fit rendering context onto the resizable screen */ + if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { + float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT; + int w = (int)((float)RENDER_BASE_WIDTH * ratio); + glViewport( + ctx.window_w / 2 - w / 2, + 0, + w, + ctx.window_h + ); + } else { + float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH; + int h = (int)((float)RENDER_BASE_HEIGHT * ratio); + glViewport( + 0, + ctx.window_h / 2 - h / 2, + ctx.window_w, + h + ); + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_ALPHA_TEST); + glClearColor((1.0f / 255) * 230, (1.0f / 255) * 230, (1.0f / 255) * 230, 1); @@ -511,22 +553,42 @@ void render(void) { GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - // glDisable(GL_CULL_FACE); - // glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); + { + glDisable(GL_CULL_FACE); + glDepthFunc(GL_ALWAYS); /* fill depth buffer with ones, 2d view is always in front */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); - /* TODO: write with no depth test, just fill it in */ - render_sprites(); - render_rectangles(); - render_circles(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - // glEnable(GL_CULL_FACE); - // glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + render_sprites(); + render_rectangles(); + render_circles(); + } - /* TODO: use depth test to optimize gui regions away */ - render_space(); + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotatef(60, 1, 0, 0); + + glEnable(GL_CULL_FACE); + glDepthFunc(GL_LESS); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* TODO: use depth test to optimize gui regions away */ + render_space(); + } - SDL_RenderFlush(ctx.renderer); SDL_GL_SwapWindow(ctx.window); } diff --git a/src/textures.c b/src/textures.c index c202ff2..37cc773 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1,6 +1,7 @@ -#include "textures.h" +#include "private/textures.h" #include "config.h" #include "util.h" +#include "textures.h" #include #include @@ -35,6 +36,24 @@ fail: } +static GLuint new_gl_texture(void) { + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glBindTexture(GL_TEXTURE_2D, 0); + return texture; +} + + /* adds a new, blank atlas surface to the cache */ static void add_new_atlas(struct texture_cache *cache) { SDL_PixelFormat *native_format = @@ -57,12 +76,51 @@ static void add_new_atlas(struct texture_cache *cache) { a_mask); SDL_FreeFormat(native_format); - SDL_SetSurfaceRLE(new_atlas, true); + SDL_SetSurfaceRLE(new_atlas, true); arrput(cache->atlas_surfaces, new_atlas); - - SDL_Texture *new_atlas_texture = - SDL_CreateTextureFromSurface(cache->renderer, new_atlas); - arrput(cache->atlas_textures, new_atlas_texture); + arrput(cache->atlas_textures, new_gl_texture()); +} + + +static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) { + Uint32 rmask, gmask, bmask, amask; + + glBindTexture(GL_TEXTURE_2D, texture); + + // glPixelStorei(GL_PACK_ALIGNMENT, 1); + + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; + #else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + #endif + + /* TODO: don't do it if format is compatible */ + SDL_Surface* intermediate = SDL_CreateRGBSurface(0, + surface->w, surface->h, 32, rmask, gmask, bmask, amask); + SDL_BlitSurface(surface, NULL, intermediate, NULL); + SDL_LockSurface(intermediate); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + surface->w, + surface->h, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + intermediate->pixels); + + SDL_UnlockSurface(intermediate); + SDL_FreeSurface(intermediate); + + glBindTexture(GL_TEXTURE_2D, 0); } @@ -85,12 +143,7 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { } /* texturize it! */ - SDL_LockSurface(atlas_surface); - SDL_UpdateTexture(cache->atlas_textures[cache->atlas_index], - NULL, - atlas_surface->pixels, - atlas_surface->pitch); - SDL_UnlockSurface(atlas_surface); + upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface); } @@ -171,7 +224,6 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { cache->window = window; - cache->renderer = SDL_GetRenderer(window); sh_new_arena(cache->hash); sh_new_arena(cache->loner_hash); @@ -185,7 +237,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { void textures_cache_deinit(struct texture_cache *cache) { /* free atlas textures */ for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) { - SDL_DestroyTexture(cache->atlas_textures[i]); + glDeleteTextures(1, &cache->atlas_textures[i]); } arrfree(cache->atlas_textures); @@ -247,8 +299,11 @@ void textures_load(struct texture_cache *cache, const char *path) { /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { - new_texture.loner_data = SDL_CreateTextureFromSurface(cache->renderer, surface); + new_texture.loner_data = new_gl_texture(); + upload_texture_from_surface(new_texture.loner_data, surface); new_texture.atlas_index = -1; + new_texture.srcrect = (SDL_Rect) { + .w = surface->w, .h = surface->h }; shput(cache->loner_hash, path, new_texture); } else { new_texture.atlas_index = cache->atlas_index; @@ -301,17 +356,23 @@ void textures_update_current_atlas(struct texture_cache *cache) { SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path) { - struct texture_cache_item *texture = shgetp_null(cache->hash, path); - if (texture == NULL) { + int index = textures_get_atlas_index(cache, path); + if (index == -1) { + return shget(cache->loner_hash, path).srcrect; + } else if (index == INT_MIN) { CRY("Texture lookup failed.", "Tried to get texture that isn't loaded."); return (SDL_Rect){ 0, 0, 0, 0 }; + } else { + return shget(cache->hash, path).srcrect; } - - return texture->value.srcrect; } +/* TODO: instead of index, return a 'key' with following encoded meaning: */ +/* value of 0 - no atlas (#define NO_ATLAS (0) ?) */ +/* negative value - index in loners (-key - 1) */ +/* positive value - index in atlases (key - 1) */ int textures_get_atlas_index(struct texture_cache *cache, const char *path) { struct texture_cache_item *texture = shgetp_null(cache->hash, path); @@ -331,25 +392,28 @@ int textures_get_atlas_index(struct texture_cache *cache, const char *path) { } -SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index) { +void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target) { /* out of bounds */ - if (arrlen(cache->atlas_textures) < index + 1 || index < 0) - return NULL; + if (arrlen(cache->atlas_textures) < index + 1 || index < 0) { + CRY("Atlas texture binding failed.", + "Tried to bind texture by invalid index"); + return; + } - return cache->atlas_textures[index]; + glBindTexture(target, cache->atlas_textures[index]); } -SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path) { +void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target) { struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path); if (texture == NULL) { - CRY("Loner texture lookup failed.", - "Tried to get texture that isn't loaded."); - return NULL; + CRY("Loner texture binding failed.", + "Tried to bind texture that isn't loaded."); + return; } - return texture->value.loner_data; + glBindTexture(target, texture->value.loner_data); } diff --git a/src/textures.h b/src/textures.h index 7d6c7ea..d6a5b58 100644 --- a/src/textures.h +++ b/src/textures.h @@ -1,46 +1,10 @@ #ifndef TEXTURES_H #define TEXTURES_H +#include "private/textures.h" #include -#include - -#include - - -struct texture { - SDL_Rect srcrect; /* position in atlas */ - SDL_Surface *data; /* original image data */ - SDL_Texture *loner_data; /* loner textures store their data directly */ - int atlas_index; /* which atlas the texture is in */ - int8_t layer; -}; - - -struct texture_cache_item { - char *key; - struct texture value; -}; - - -/* use the public API to create and manipulate instances of this structure */ -struct texture_cache { - /* from context */ - SDL_Window *window; - SDL_Renderer *renderer; - - struct texture_cache_item *hash; - struct texture_cache_item *loner_hash; - - stbrp_node *node_buffer; /* used internally by stb_rect_pack */ - - SDL_Surface **atlas_surfaces; - SDL_Texture **atlas_textures; - int atlas_index; - - bool is_dirty; /* current atlas needs to be recreated */ -}; - +#include void textures_cache_init(struct texture_cache *cache, SDL_Window *window); void textures_cache_deinit(struct texture_cache *cache); @@ -64,15 +28,12 @@ SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path); /* if the texture is not found, returns INT_MIN */ int textures_get_atlas_index(struct texture_cache *cache, const char *path); -/* returns a pointer to the atlas at `index` */ -/* if the index is out of bounds, returns NULL. */ -/* you can get the index via texture_get_atlas_index */ -SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index); +/* binds atlas texture in opengl state */ +void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target); -SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path); +void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target); /* returns the number of atlases in the cache */ size_t textures_get_num_atlases(struct texture_cache *cache); - #endif -- 2.30.2 From bd53a931c00708d8c78f7a382620e94b8beeaae3 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 14 Jul 2024 16:04:12 +0300 Subject: [PATCH 05/27] rework of textures, finalization of basic opengl rendering --- .clangd | 2 + CMakeLists.txt | 2 +- src/context.h | 4 +- src/private/rendering.h | 45 ++++++--- src/private/textures.h | 15 +-- src/rendering.c | 216 ++++++++++++++++------------------------ src/textures.c | 136 ++++++++++++------------- src/textures.h | 38 ++++--- src/util.c | 15 +++ src/util.h | 15 ++- 10 files changed, 243 insertions(+), 245 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c835fb9 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: "./.build/" diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a427f3..5dd55ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES}) set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED ON - C_EXTENSIONS OFF) + C_EXTENSIONS ON) # extensions are required by stb_ds.h # distribution definitions set(ORGANIZATION_NAME "wanp" CACHE STRING diff --git a/src/context.h b/src/context.h index bf31e21..d26746f 100644 --- a/src/context.h +++ b/src/context.h @@ -23,9 +23,7 @@ typedef struct context { struct sprite_primitive *render_queue_sprites; struct rect_primitive *render_queue_rectangles; struct circle_primitive *render_queue_circles; - - struct mesh_batch *uncolored_mesh_batches; /* texture_cache reflected */ - struct mesh_batch_item *uncolored_mesh_batches_loners; /* path reflected */ + struct mesh_batch_item *uncolored_mesh_batches; struct audio_channel_item *audio_channels; SDL_AudioDeviceID audio_device; diff --git a/src/private/rendering.h b/src/private/rendering.h index 75b662f..5c3326e 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -2,6 +2,7 @@ #define PRIVATE_RENDERING_H #include "../rendering.h" +#include "../textures.h" #include "../util.h" #include @@ -13,9 +14,8 @@ struct sprite_primitive { t_frect rect; t_color color; double rotation; - char *path; SDL_BlendMode blend_mode; - int atlas_index; + t_texture_key texture_key; int layer; bool flip_x; bool flip_y; @@ -32,26 +32,39 @@ struct circle_primitive { t_fvec2 position; }; +/* union for in-place recalculation of texture coordinates */ +union uncolored_space_triangle { + /* pending for sending, uvs are not final as texture atlases could update */ + struct uncolored_space_triangle_primitive { + t_fvec3 v0; + t_fvec2 uv0; /* in pixels */ + t_fvec3 v1; + t_fvec2 uv1; /* in pixels */ + t_fvec3 v2; + t_fvec2 uv2; /* in pixels */ + } primitive; + + /* structure that is passed in opengl vertex array */ + struct uncolored_space_triangle_payload { + t_fvec3 v0; + t_fvec2 uv0; + t_fvec3 v1; + t_fvec2 uv1; + t_fvec3 v2; + t_fvec2 uv2; + } payload; +}; + /* batch of primitives with overlapping properties */ struct mesh_batch { - GLuint buffer; /* server side storage */ - uint8_t *data; /* client side storage */ - // size_t buffer_len; /* element count */ + GLuint buffer; /* server side storage */ + size_t buffer_len; /* element count */ + uint8_t *primitives; }; struct mesh_batch_item { - char *key; + t_texture_key key; struct mesh_batch value; }; -/* is structure that is in opengl vertex array */ -struct uncolored_space_triangle { - t_fvec3 v0; - t_fvec2 uv0; - t_fvec3 v1; - t_fvec2 uv1; - t_fvec3 v2; - t_fvec2 uv2; -}; - #endif diff --git a/src/private/textures.h b/src/private/textures.h index 87b49d4..f7abb32 100644 --- a/src/private/textures.h +++ b/src/private/textures.h @@ -1,6 +1,8 @@ #ifndef PRIVATE_TEXTURES_H #define PRIVATE_TEXTURES_H +#include "../util.h" + #include #include #include @@ -8,10 +10,10 @@ #include struct texture { - SDL_Rect srcrect; /* position in atlas */ + t_rect srcrect; /* position in atlas */ SDL_Surface *data; /* original image data */ - GLuint loner_data; /* loner textures store their data directly */ - int atlas_index; /* which atlas the texture is in */ + int atlas_index; + GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used*/ int8_t layer; }; @@ -27,15 +29,14 @@ struct texture_cache { SDL_Window *window; struct texture_cache_item *hash; - struct texture_cache_item *loner_hash; - stbrp_node *node_buffer; /* used internally by stb_rect_pack */ + stbrp_node *node_buffer; /* used internally by stb_rect_pack */ SDL_Surface **atlas_surfaces; - GLuint *atlas_textures; + GLuint *atlas_textures; /* shared by atlas textures */ int atlas_index; - bool is_dirty; /* current atlas needs to be recreated */ + bool is_dirty; /* current atlas needs to be recreated */ }; #endif diff --git a/src/rendering.c b/src/rendering.c index 508835a..e9d5786 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -20,11 +20,8 @@ void render_queue_clear(void) { arrsetlen(ctx.render_queue_rectangles, 0); arrsetlen(ctx.render_queue_circles, 0); - for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) - arrsetlen(ctx.uncolored_mesh_batches[i].data, 0); - - for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) - arrsetlen(ctx.uncolored_mesh_batches_loners[i].value.data, 0); + for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) + arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0); } @@ -37,16 +34,12 @@ void render_queue_clear(void) { /* sprite */ void push_sprite(char *path, t_frect rect) { - textures_load(&ctx.texture_cache, path); - struct sprite_primitive sprite = { .rect = rect, .color = (t_color) { 255, 255, 255, 255 }, - .path = path, .rotation = 0.0, .blend_mode = SDL_BLENDMODE_BLEND, - .atlas_index = - textures_get_atlas_index(&ctx.texture_cache, path), + .texture_key = textures_get_key(&ctx.texture_cache, path), .layer = 0, .flip_x = false, .flip_y = false, @@ -57,16 +50,12 @@ void push_sprite(char *path, t_frect rect) { void push_sprite_ex(t_frect rect, t_push_sprite_args args) { - textures_load(&ctx.texture_cache, args.path); - struct sprite_primitive sprite = { .rect = rect, .color = args.color, - .path = args.path, .rotation = args.rotation, .blend_mode = args.blend_mode, - .atlas_index = - textures_get_atlas_index(&ctx.texture_cache, args.path), + .texture_key = textures_get_key(&ctx.texture_cache, args.path), .layer = args.layer, .flip_x = args.flip_x, .flip_y = args.flip_y, @@ -100,6 +89,7 @@ void push_circle(t_fvec2 position, float radius, t_color color) { /* TODO: automatic handling of repeating textures */ +/* for that we could allocate a loner texture */ void unfurl_triangle(const char *path, t_fvec3 v0, t_fvec3 v1, @@ -108,70 +98,35 @@ void unfurl_triangle(const char *path, t_shvec2 uv1, t_shvec2 uv2) { - /* corrected atlas texture coordinates */ - t_fvec2 uv0c, uv1c, uv2c; - struct mesh_batch *batch_p; + const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); - textures_load(&ctx.texture_cache, path); - const SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, path); - - const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path); - if (atlas_index == -1) { - uv0c.x = (float)uv0.x / (float)srcrect.w; - uv0c.y = (float)uv0.y / (float)srcrect.h; - uv1c.x = (float)uv1.x / (float)srcrect.w; - uv1c.y = (float)uv1.y / (float)srcrect.h; - uv2c.x = (float)uv2.x / (float)srcrect.w; - uv2c.y = (float)uv2.y / (float)srcrect.h; - - batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value; - - } else { - const size_t old_len = arrlenu(ctx.uncolored_mesh_batches); - if ((size_t)atlas_index + 1 >= old_len) { - /* grow to accommodate texture cache atlases */ - arrsetlen(ctx.uncolored_mesh_batches, atlas_index + 1); - - /* zero initialize it all, it's a valid state */ - SDL_memset(&ctx.uncolored_mesh_batches[atlas_index], - 0, - sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len)); - } - - const float wr = (float)srcrect.w / TEXTURE_ATLAS_SIZE; - const float hr = (float)srcrect.h / TEXTURE_ATLAS_SIZE; - const float xr = (float)srcrect.x / TEXTURE_ATLAS_SIZE; - const float yr = (float)srcrect.y / TEXTURE_ATLAS_SIZE; - - uv0c.x = xr + ((float)uv0.x / (float)srcrect.w) * wr; - uv0c.y = yr + ((float)uv0.y / (float)srcrect.h) * hr; - uv1c.x = xr + ((float)uv1.x / (float)srcrect.w) * wr; - uv1c.y = yr + ((float)uv1.y / (float)srcrect.h) * hr; - uv2c.x = xr + ((float)uv2.x / (float)srcrect.w) * wr; - uv2c.y = yr + ((float)uv2.y / (float)srcrect.h) * hr; - - batch_p = &ctx.uncolored_mesh_batches[atlas_index]; + struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); + if (!batch_p) { + struct mesh_batch item = {0}; + hmput(ctx.uncolored_mesh_batches, texture_key, item); + batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ } - struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data; - struct uncolored_space_triangle pack = { + union uncolored_space_triangle triangle = { .primitive = { .v0 = v0, - .uv0 = uv0c, .v1 = v1, - .uv1 = uv1c, .v2 = v2, - .uv2 = uv2c, - }; - arrpush(data, pack); + .uv1 = m_to_fvec2(uv1), + .uv0 = m_to_fvec2(uv0), + .uv2 = m_to_fvec2(uv2), + }}; - batch_p->data = (uint8_t *)data; + union uncolored_space_triangle *triangles = + (union uncolored_space_triangle *)batch_p->value.primitives; + arrpush(triangles, triangle); + batch_p->value.primitives = (uint8_t *)triangles; } /* compare functions for the sort in render_sprites */ static int cmp_atlases(const void *a, const void *b) { - int index_a = ((const struct sprite_primitive *)a)->atlas_index; - int index_b = ((const struct sprite_primitive *)b)->atlas_index; + int index_a = ((const struct sprite_primitive *)a)->texture_key.id; + int index_b = ((const struct sprite_primitive *)b)->texture_key.id; return (index_a > index_b) - (index_a < index_b); } @@ -259,40 +214,28 @@ static void render_sprite(struct sprite_primitive *sprite) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); - /* loner */ - if (sprite->atlas_index == -1) { - textures_bind_loner(&ctx.texture_cache, sprite->path, GL_TEXTURE_2D); - glTexCoordPointer(2, - GL_SHORT, - 0, - (void *)(int16_t[6 * 2]) { - 0, INT16_MAX, - 0, 0, - INT16_MAX, 0, - INT16_MAX, 0, - INT16_MAX, INT16_MAX, - 0, INT16_MAX }); - } else { - SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path); - textures_bind_atlas(&ctx.texture_cache, sprite->atlas_index, GL_TEXTURE_2D); - glTexCoordPointer(2, - GL_FLOAT, - 0, - /* TODO: try using shorts */ - (void *)(float[6 * 2]) { - (float)srcrect.x / TEXTURE_ATLAS_SIZE, - (float)srcrect.y / TEXTURE_ATLAS_SIZE, - (float)srcrect.x / TEXTURE_ATLAS_SIZE, - (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, - (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, - (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, - (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, - (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, - (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, - (float)srcrect.y / TEXTURE_ATLAS_SIZE, - (float)srcrect.x / TEXTURE_ATLAS_SIZE, - (float)srcrect.y / TEXTURE_ATLAS_SIZE }); - } + textures_bind(&ctx.texture_cache, sprite->texture_key, GL_TEXTURE_2D); + + t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->texture_key); + t_rect dims = textures_get_dims(&ctx.texture_cache, sprite->texture_key); + + glTexCoordPointer(2, + GL_FLOAT, + 0, + /* TODO: try using shorts */ + (void *)(float[6 * 2]) { + (float)srcrect.x / (float)dims.w, + (float)srcrect.y / (float)dims.h, + (float)srcrect.x / (float)dims.w, + (float)(srcrect.y + srcrect.h) / (float)dims.h, + (float)(srcrect.x + srcrect.w) / (float)dims.w, + (float)(srcrect.y + srcrect.h) / (float)dims.h, + (float)(srcrect.x + srcrect.w) / (float)dims.w, + (float)(srcrect.y + srcrect.h) / (float)dims.h, + (float)(srcrect.x + srcrect.w) / (float)dims.w, + (float)srcrect.y / (float)dims.h, + (float)srcrect.x / (float)dims.w, + (float)srcrect.y / (float)dims.h }); glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a); @@ -446,41 +389,69 @@ static void render_circles(void) { } -static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) { - size_t data_len = arrlenu(batch->data); +static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, + t_texture_key texture_key) +{ + size_t primitives_len = arrlenu(batch->primitives); + + if (primitives_len == 0) + return; /* create vertex array object */ if (batch->buffer == 0) glGenBuffers(1, &batch->buffer); - glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); - /* TODO: try using mapped buffers while building batches instead? */ /* this way we could skip client side copy that is kept until commitment */ /* alternatively we could commit glBufferSubData based on a threshold */ + /* update pixel-based uvs to correspond with texture atlases */ + for (size_t i = 0; i < primitives_len; ++i) { + struct uncolored_space_triangle_payload *payload = + &((union uncolored_space_triangle *)batch->primitives)[i].payload; + + t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); + t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); + + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; + + payload->uv0.x = xr + ((float)payload->uv0.x / (float)srcrect.w) * wr; + payload->uv0.y = yr + ((float)payload->uv0.y / (float)srcrect.h) * hr; + payload->uv1.x = xr + ((float)payload->uv1.x / (float)srcrect.w) * wr; + payload->uv1.y = yr + ((float)payload->uv1.y / (float)srcrect.h) * hr; + payload->uv2.x = xr + ((float)payload->uv2.x / (float)srcrect.w) * wr; + payload->uv2.y = yr + ((float)payload->uv2.y / (float)srcrect.h) * hr; + } + + textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D); + + glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); + /* upload batched data */ glBufferData(GL_ARRAY_BUFFER, - data_len * sizeof (struct uncolored_space_triangle), - batch->data, + primitives_len * sizeof (struct uncolored_space_triangle_payload), + batch->primitives, GL_STREAM_DRAW); /* vertex specification*/ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, - offsetof(struct uncolored_space_triangle, v1), - (void *)offsetof(struct uncolored_space_triangle, v0)); + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, v0)); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, - offsetof(struct uncolored_space_triangle, v1), - (void *)offsetof(struct uncolored_space_triangle, uv0)); + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); /* commit for drawing */ - glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len); + glDrawArrays(GL_TRIANGLES, 0, 3 * (int)primitives_len); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -498,28 +469,15 @@ static void render_space(void) { /* solid white, no modulation */ glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) { - if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) { - textures_bind_atlas(&ctx.texture_cache, (int)i, GL_TEXTURE_2D); - draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]); - } - } - - for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) { - if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) { - textures_bind_loner(&ctx.texture_cache, - ctx.uncolored_mesh_batches_loners[i].key, - GL_TEXTURE_2D); - - draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value); - } + for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) { + draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value, + ctx.uncolored_mesh_batches[i].key); } } void render(void) { - if (ctx.texture_cache.is_dirty) - textures_update_current_atlas(&ctx.texture_cache); + textures_update_atlas(&ctx.texture_cache); /* fit rendering context onto the resizable screen */ if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { diff --git a/src/textures.c b/src/textures.c index 37cc773..1ee060d 100644 --- a/src/textures.c +++ b/src/textures.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -139,7 +138,12 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { SDL_BlitSurface(cache->hash[i].value.data, NULL, atlas_surface, - &cache->hash[i].value.srcrect); + &(SDL_Rect){ + .x = cache->hash[i].value.srcrect.x, + .y = cache->hash[i].value.srcrect.y, + .w = cache->hash[i].value.srcrect.w, + .h = cache->hash[i].value.srcrect.h, + }); } /* texturize it! */ @@ -212,7 +216,7 @@ static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_r /* updates the atlas location of every rect in the cache */ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { for (size_t i = 0; i < arrlenu(rects); ++i) { - cache->hash[i].value.srcrect = (SDL_Rect) { + cache->hash[i].value.srcrect = (t_rect) { .x = rects[i].x, .y = rects[i].y, .w = rects[i].w, @@ -225,7 +229,6 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { cache->window = window; sh_new_arena(cache->hash); - sh_new_arena(cache->loner_hash); cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE); @@ -253,11 +256,6 @@ void textures_cache_deinit(struct texture_cache *cache) { } shfree(cache->hash); - for (size_t i = 0; i < shlenu(cache->loner_hash); ++i) { - SDL_FreeSurface(cache->loner_hash[i].value.data); - } - shfree(cache->loner_hash); - free(cache->node_buffer); } @@ -282,38 +280,38 @@ void textures_dump_atlases(struct texture_cache *cache) { IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true); log_info("Dumped atlas %s", buf); } - - size_t num_loners = shlenu(cache->loner_hash); - log_info("%zd atlases dumped. %zd loners left undumped.", i, num_loners); } -void textures_load(struct texture_cache *cache, const char *path) { +static t_texture_key textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ - if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0) - return; + if (shgeti(cache->hash, path) >= 0) + return (t_texture_key){0}; SDL_Surface *surface = image_to_surface(path); - struct texture new_texture; + struct texture new_texture = {0}; new_texture.data = surface; /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { - new_texture.loner_data = new_gl_texture(); - upload_texture_from_surface(new_texture.loner_data, surface); - new_texture.atlas_index = -1; - new_texture.srcrect = (SDL_Rect) { - .w = surface->w, .h = surface->h }; - shput(cache->loner_hash, path, new_texture); + new_texture.loner_texture = new_gl_texture(); + upload_texture_from_surface(new_texture.loner_texture, surface); + new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; + shput(cache->hash, path, new_texture); + return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; + return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; } } -void textures_update_current_atlas(struct texture_cache *cache) { +void textures_update_atlas(struct texture_cache *cache) { + if (!cache->is_dirty) + return; + /* this function makes a lot more sense if you read stb_rect_pack.h */ stbrp_context pack_ctx; /* target info */ stbrp_init_target(&pack_ctx, @@ -355,68 +353,56 @@ void textures_update_current_atlas(struct texture_cache *cache) { } -SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path) { - int index = textures_get_atlas_index(cache, path); - if (index == -1) { - return shget(cache->loner_hash, path).srcrect; - } else if (index == INT_MIN) { +t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { + /* hash tables are assumed to be stable, so we just return indices */ + ptrdiff_t texture = shgeti(cache->hash, path); + + /* load it if it isn't */ + if (texture == -1) { + return textures_load(cache, path); + } else + return (t_texture_key){ (int)texture + 1 }; +} + + +t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { + if (key.id != 0) { + return cache->hash[key.id - 1].value.srcrect; + } else { CRY("Texture lookup failed.", "Tried to get texture that isn't loaded."); - return (SDL_Rect){ 0, 0, 0, 0 }; + return (t_rect){ 0, 0, 0, 0 }; + } +} + + +t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { + if (key.id != 0) { + if (cache->hash[key.id - 1].value.loner_texture != 0) + return cache->hash[key.id - 1].value.srcrect; + else + return (t_rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; } else { - return shget(cache->hash, path).srcrect; + CRY("Texture lookup failed.", + "Tried to get texture that isn't loaded."); + return (t_rect){ 0, 0, 0, 0 }; } } -/* TODO: instead of index, return a 'key' with following encoded meaning: */ -/* value of 0 - no atlas (#define NO_ATLAS (0) ?) */ -/* negative value - index in loners (-key - 1) */ -/* positive value - index in atlases (key - 1) */ -int textures_get_atlas_index(struct texture_cache *cache, const char *path) { - struct texture_cache_item *texture = shgetp_null(cache->hash, path); - - /* it might be a loner texture */ - if (texture == NULL) { - texture = shgetp_null(cache->loner_hash, path); - - /* never mind it's just not there at all */ - if (texture == NULL) { - CRY("Texture atlas index lookup failed.", - "Tried to get atlas index of texture that isn't loaded."); - return INT_MIN; - } +void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target) { + if (key.id != 0) { + if (cache->hash[key.id - 1].value.loner_texture == 0) + glBindTexture(target, cache->atlas_textures[cache->hash[key.id - 1].value.atlas_index]); + else + glBindTexture(target, cache->hash[key.id - 1].value.loner_texture); + } else if (key.id == 0) { + CRY("Texture binding failed.", + "Tried to get texture that isn't loaded."); } - - return texture->value.atlas_index; } -void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target) { - /* out of bounds */ - if (arrlen(cache->atlas_textures) < index + 1 || index < 0) { - CRY("Atlas texture binding failed.", - "Tried to bind texture by invalid index"); - return; - } - - glBindTexture(target, cache->atlas_textures[index]); -} - - -void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target) { - struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path); - - if (texture == NULL) { - CRY("Loner texture binding failed.", - "Tried to bind texture that isn't loaded."); - return; - } - - glBindTexture(target, texture->value.loner_data); -} - - -size_t textures_get_num_atlases(struct texture_cache *cache) { +size_t textures_get_num_atlases(const struct texture_cache *cache) { return cache->atlas_index + 1; } diff --git a/src/textures.h b/src/textures.h index d6a5b58..b244208 100644 --- a/src/textures.h +++ b/src/textures.h @@ -2,10 +2,20 @@ #define TEXTURES_H #include "private/textures.h" +#include "util.h" #include #include +/* type safe structure for persistent texture handles */ +typedef struct { int id; } t_texture_key; + +/* tests whether given key structure corresponds to any texture */ +#define m_texture_key_is_valid(p_key) ((p_key).id != 0) + +/* tests whether given key corresponds to atlas texture or a loner */ +#define m_texture_is_atlas(p_key) ((p_key).id > 0) + void textures_cache_init(struct texture_cache *cache, SDL_Window *window); void textures_cache_deinit(struct texture_cache *cache); @@ -15,25 +25,27 @@ void textures_dump_atlases(struct texture_cache *cache); /* loads an image if it isn't in the cache, otherwise a no-op. */ /* can be called from anywhere at any time after init, useful if you want to */ /* preload textures you know will definitely be used */ -void textures_load(struct texture_cache *cache, const char *path); +// void textures_load(struct texture_cache *cache, const char *path); -/* repacks the current texture atlas based on the texture cache */ -void textures_update_current_atlas(struct texture_cache *cache); +/* repacks the current texture atlas based on the texture cache if needed */ +/* any previously returned srcrect results are invalidated after that */ +/* call it every time before rendering */ +void textures_update_atlas(struct texture_cache *cache); -/* returns a rect in a texture cache atlas based on a path, for drawing */ -/* if the texture is not found, returns a zero-filled rect (so check w or h) */ -SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path); +/* returns a persistent handle to some texture in cache, loading it if needed */ +/* check the result with m_texture_key_is_valid() */ +t_texture_key textures_get_key(struct texture_cache *cache, const char *path); -/* returns which atlas the texture in the path is in, starting from 0 */ -/* if the texture is not found, returns INT_MIN */ -int textures_get_atlas_index(struct texture_cache *cache, const char *path); +/* returns a rect in a texture cache of the given key */ +t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); + +/* returns a rect of dimensions of the whole texture (whole atlas) */ +t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key); /* binds atlas texture in opengl state */ -void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target); - -void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target); +void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); /* returns the number of atlases in the cache */ -size_t textures_get_num_atlases(struct texture_cache *cache); +size_t textures_get_num_atlases(const struct texture_cache *cache); #endif diff --git a/src/util.c b/src/util.c index f3d4730..9e49a64 100644 --- a/src/util.c +++ b/src/util.c @@ -186,6 +186,21 @@ t_frect to_frect(t_rect rect) { } +t_fvec2 fvec2_from_vec2(t_vec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + +t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + + void tick_timer(int *value) { *value = MAX(*value - 1, 0); } diff --git a/src/util.h b/src/util.h index 34c69df..03be517 100644 --- a/src/util.h +++ b/src/util.h @@ -48,7 +48,9 @@ void *ccalloc(size_t num, size_t size); #define MAX SDL_max #define MIN SDL_min +#ifndef M_PI #define M_PI 3.14159265358979323846264338327950288 /**< pi */ +#endif /* sets buf_out to a pointer to a byte buffer which must be freed. */ /* returns the size of this buffer. */ @@ -94,6 +96,7 @@ typedef struct frect { bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); +/* TODO: generics and specials (see m_to_fvec2() for an example)*/ t_frect to_frect(t_rect rect); @@ -122,6 +125,17 @@ typedef struct shvec2 { } t_shvec2; +/* aren't macros to prevent double evaluation with side effects */ +/* maybe could be inlined? i hope LTO will resolve this */ +t_fvec2 fvec2_from_vec2(t_vec2 vec); +t_fvec2 fvec2_from_shvec2(t_shvec2 vec); + +#define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ + t_vec2: fvec2_from_vec2, \ + t_shvec2: fvec2_from_shvec2 \ + )(p_any_vec2)) + + /* decrements an lvalue (which should be an int), stopping at 0 */ /* meant for tick-based timers in game logic */ /* @@ -130,7 +144,6 @@ typedef struct shvec2 { */ void tick_timer(int *value); - /* decrements a floating point second-based timer, stopping at 0.0 */ /* meant for poll based real time logic in game logic */ /* note that it should be decremented only on the next tick after its creation */ -- 2.30.2 From 7218acb40b56c144379d368ef2e132ab142ac09f Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 14 Jul 2024 18:36:48 +0300 Subject: [PATCH 06/27] textures.c: fix regressions for loner textures --- src/game/scenes/ingame.c | 12 ++++++------ src/private/textures.h | 5 ++--- src/rendering.c | 1 - src/textures.c | 13 +++++++++++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 66e36f9..21bdec3 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,20 +11,20 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - unfurl_triangle("/assets/player/baron-walk.png", + unfurl_triangle("/assets/big-violet.png", (t_fvec3){ -1, -1, 0 }, (t_fvec3){ 1, -1, 0 }, (t_fvec3){ 1, 1, 0 }, (t_shvec2){ 0, 0 }, - (t_shvec2){ 48, 0 }, - (t_shvec2){ 48, 48 }); + (t_shvec2){ 2048, 0 }, + (t_shvec2){ 2048, 2048 }); - unfurl_triangle("/assets/player/baron-walk.png", + unfurl_triangle("/assets/big-violet.png", (t_fvec3){ 1, 1, 0 }, (t_fvec3){ -1, 1, 0 }, (t_fvec3){ -1, -1, 0 }, - (t_shvec2){ 48, 48 }, - (t_shvec2){ 0, 48 }, + (t_shvec2){ 2048, 2048 }, + (t_shvec2){ 0, 2048 }, (t_shvec2){ 0, 0 }); } diff --git a/src/private/textures.h b/src/private/textures.h index f7abb32..6f5d727 100644 --- a/src/private/textures.h +++ b/src/private/textures.h @@ -25,8 +25,7 @@ struct texture_cache_item { struct texture_cache { - /* from context */ - SDL_Window *window; + SDL_Window *window; /* from context */ struct texture_cache_item *hash; @@ -34,7 +33,7 @@ struct texture_cache { SDL_Surface **atlas_surfaces; GLuint *atlas_textures; /* shared by atlas textures */ - int atlas_index; + int atlas_index; /* atlas that is currently being built */ bool is_dirty; /* current atlas needs to be recreated */ }; diff --git a/src/rendering.c b/src/rendering.c index e9d5786..eeed4ea 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -537,7 +537,6 @@ void render(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glRotatef(60, 1, 0, 0); glEnable(GL_CULL_FACE); glDepthFunc(GL_LESS); diff --git a/src/textures.c b/src/textures.c index 1ee060d..e10ff1a 100644 --- a/src/textures.c +++ b/src/textures.c @@ -125,6 +125,7 @@ static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) { static void recreate_current_atlas_texture(struct texture_cache *cache) { /* TODO: figure out if SDL_UpdateTexture alone is faster than blitting */ + /* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */ SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index]; /* clear */ @@ -132,9 +133,14 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { /* blit the texture surfaces onto the atlas */ for (size_t i = 0; i < shlenu(cache->hash); ++i) { + /* skip all that aren't part of currently built one */ if (cache->hash[i].value.atlas_index != cache->atlas_index) continue; + /* skip loners */ + if (cache->hash[i].value.loner_texture != 0) + continue; + SDL_BlitSurface(cache->hash[i].value.data, NULL, atlas_surface, @@ -155,7 +161,10 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) { stbrp_rect *rects = NULL; for (size_t i = 0; i < shlenu(cache->hash); ++i) { - SDL_Surface *surface_data = cache->hash[i].value.data; + if (cache->hash[i].value.loner_texture != 0) + continue; + + const SDL_Surface *surface_data = cache->hash[i].value.data; stbrp_rect new_rect = { .w = surface_data->w, .h = surface_data->h, @@ -293,7 +302,7 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path new_texture.data = surface; /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ - if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { + if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) { new_texture.loner_texture = new_gl_texture(); upload_texture_from_surface(new_texture.loner_texture, surface); new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; -- 2.30.2 From 5ae59b51d38e84c61bf2be8f93bf001511215dff Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 14 Jul 2024 19:18:10 +0300 Subject: [PATCH 07/27] textures.h: cleanup, writeup for unfurl_billboard() --- data/assets/big-violet.png | 3 +++ src/private/textures.h | 1 - src/rendering.h | 8 +++++--- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 data/assets/big-violet.png diff --git a/data/assets/big-violet.png b/data/assets/big-violet.png new file mode 100644 index 0000000..84f5f75 --- /dev/null +++ b/data/assets/big-violet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7c91f67e8bd93e1abf6f40ee14a72cd0990d2e25ae30eca9c553bc1e2381af8 +size 77981 diff --git a/src/private/textures.h b/src/private/textures.h index 6f5d727..3ea2ca3 100644 --- a/src/private/textures.h +++ b/src/private/textures.h @@ -14,7 +14,6 @@ struct texture { SDL_Surface *data; /* original image data */ int atlas_index; GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used*/ - int8_t layer; }; diff --git a/src/rendering.h b/src/rendering.h index 99b0a4f..5f348e4 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -39,7 +39,6 @@ void push_circle(t_fvec2 position, float radius, t_color color); /* vertices are in absolute coordinates, relative to world origin */ /* texture coordinates are in pixels */ void unfurl_triangle(const char *path, - /* */ t_fvec3 v0, t_fvec3 v1, t_fvec3 v2, @@ -47,7 +46,6 @@ void unfurl_triangle(const char *path, t_shvec2 uv1, t_shvec2 uv2); -/* TODO: */ /* pushes a colored textured 3d triangle onto the render queue */ // void unfurl_colored_triangle(const char *path, // t_fvec3 v0, @@ -60,8 +58,12 @@ void unfurl_triangle(const char *path, // t_color c1, // t_color c2); -/* TODO: billboarding */ +// TODO: // http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 +// void unfurl_billboard(const char *path, +// t_fvec3 position, +// t_fvec2 scaling, +// t_frect uvs); /* renders the background, then the primitives in all render queues */ void render(void); -- 2.30.2 From bdf2a541076e089c21504b665af685d4b0b4aa6e Mon Sep 17 00:00:00 2001 From: wanp Date: Mon, 15 Jul 2024 23:31:54 -0300 Subject: [PATCH 08/27] replace categorized, sorted render queues with a single ordered 2d queue --- src/context.h | 6 +- src/main.c | 19 +++-- src/private/rendering.h | 17 ++++- src/rendering.c | 151 ++++++++++++---------------------------- src/rendering.h | 1 - 5 files changed, 71 insertions(+), 123 deletions(-) diff --git a/src/context.h b/src/context.h index d26746f..b046c97 100644 --- a/src/context.h +++ b/src/context.h @@ -20,9 +20,7 @@ typedef struct context { struct texture_cache texture_cache; struct input_state input; - struct sprite_primitive *render_queue_sprites; - struct rect_primitive *render_queue_rectangles; - struct circle_primitive *render_queue_circles; + struct primitive_2d *render_queue_2d; struct mesh_batch_item *uncolored_mesh_batches; struct audio_channel_item *audio_channels; @@ -38,7 +36,7 @@ typedef struct context { int64_t prev_frame_time; int64_t desired_frametime; /* how long one tick should be */ int64_t frame_accumulator; - int64_t delta_averager_residual; + int64_t delta_averager_residual; int64_t time_averager[4]; int64_t delta_time; /* preserves real time frame delta with no manipilation */ uint64_t tick_count; diff --git a/src/main.c b/src/main.c index 38db7f4..33aae14 100644 --- a/src/main.c +++ b/src/main.c @@ -28,7 +28,7 @@ static void poll_events(void) { case SDL_QUIT: ctx.is_running = false; break; - + case SDL_WINDOWEVENT: if (e.window.windowID != ctx.window_id) break; @@ -72,7 +72,7 @@ void main_loop(void) { emscripten_cancel_main_loop(); } */ - + /* frame timer */ int64_t current_frame_time = SDL_GetPerformanceCounter(); int64_t delta_time = current_frame_time - ctx.prev_frame_time; @@ -130,7 +130,7 @@ void main_loop(void) { ctx.frame_accumulator += delta_time; /* spiral of death protection */ - if (ctx.frame_accumulator > ctx.desired_frametime * 8) { + if (ctx.frame_accumulator > ctx.desired_frametime * 8) { ctx.resync_flag = true; } @@ -203,7 +203,7 @@ static bool initialize(void) { /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); - + glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); /* TODO: */ @@ -238,14 +238,14 @@ static bool initialize(void) { } /* filesystem time */ - /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ + /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ if (!PHYSFS_init(ctx.argv[0]) || !PHYSFS_setSaneConfig(ORGANIZATION_NAME, APP_NAME, PACKAGE_EXTENSION, false, true)) { CRY_PHYSFS("Filesystem initialization failed."); goto fail; } - + /* you could change this at runtime if you wanted */ ctx.update_multiplicity = 1; @@ -287,9 +287,7 @@ static bool initialize(void) { /* rendering */ /* these are dynamic arrays and will be allocated lazily by stb_ds */ - ctx.render_queue_sprites = NULL; - ctx.render_queue_rectangles = NULL; - ctx.render_queue_circles = NULL; + ctx.render_queue_2d = NULL; ctx.circle_radius_hash = NULL; textures_cache_init(&ctx.texture_cache, ctx.window); @@ -324,8 +322,7 @@ static void clean_up(void) { input_state_deinit(&ctx.input); - arrfree(ctx.render_queue_sprites); - arrfree(ctx.render_queue_rectangles); + arrfree(ctx.render_queue_2d); textures_cache_deinit(&ctx.texture_cache); PHYSFS_deinit(); diff --git a/src/private/rendering.h b/src/private/rendering.h index 5c3326e..73a9cc2 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -16,7 +16,6 @@ struct sprite_primitive { double rotation; SDL_BlendMode blend_mode; t_texture_key texture_key; - int layer; bool flip_x; bool flip_y; }; @@ -32,6 +31,22 @@ struct circle_primitive { t_fvec2 position; }; +enum primitive_2d_type { + PRIMITIVE_2D_SPRITE, + PRIMITIVE_2D_RECT, + PRIMITIVE_2D_CIRCLE, +}; + +struct primitive_2d { + enum primitive_2d_type type; + + union { + struct sprite_primitive sprite; + struct rect_primitive rect; + struct circle_primitive circle; + }; +}; + /* union for in-place recalculation of texture coordinates */ union uncolored_space_triangle { /* pending for sending, uvs are not final as texture atlases could update */ diff --git a/src/rendering.c b/src/rendering.c index eeed4ea..17d88ae 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -16,9 +16,7 @@ void render_queue_clear(void) { /* since i don't intend to free the queues, */ /* it's faster and simpler to just "start over" */ /* and start overwriting the existing data */ - arrsetlen(ctx.render_queue_sprites, 0); - arrsetlen(ctx.render_queue_rectangles, 0); - arrsetlen(ctx.render_queue_circles, 0); + arrsetlen(ctx.render_queue_2d, 0); for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0); @@ -31,7 +29,6 @@ void render_queue_clear(void) { * because they will be called multiple times in the main loop * before anything is really rendered */ - /* sprite */ void push_sprite(char *path, t_frect rect) { struct sprite_primitive sprite = { @@ -40,12 +37,16 @@ void push_sprite(char *path, t_frect rect) { .rotation = 0.0, .blend_mode = SDL_BLENDMODE_BLEND, .texture_key = textures_get_key(&ctx.texture_cache, path), - .layer = 0, .flip_x = false, .flip_y = false, }; - arrput(ctx.render_queue_sprites, sprite); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -55,13 +56,17 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { .color = args.color, .rotation = args.rotation, .blend_mode = args.blend_mode, - .texture_key = textures_get_key(&ctx.texture_cache, args.path), - .layer = args.layer, + .texture_key = textures_get_key(&ctx.texture_cache, args.path), .flip_x = args.flip_x, .flip_y = args.flip_y, }; - arrput(ctx.render_queue_sprites, sprite); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -72,7 +77,12 @@ void push_rectangle(t_frect rect, t_color color) { .color = color, }; - arrput(ctx.render_queue_rectangles, rectangle); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_RECT, + .rect = rectangle, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -84,7 +94,12 @@ void push_circle(t_fvec2 position, float radius, t_color color) { .position = position, }; - arrput(ctx.render_queue_circles, circle); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_CIRCLE, + .circle = circle, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -123,76 +138,6 @@ void unfurl_triangle(const char *path, } -/* compare functions for the sort in render_sprites */ -static int cmp_atlases(const void *a, const void *b) { - int index_a = ((const struct sprite_primitive *)a)->texture_key.id; - int index_b = ((const struct sprite_primitive *)b)->texture_key.id; - - return (index_a > index_b) - (index_a < index_b); -} - - -static int cmp_blend_modes(const void *a, const void *b) { - SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode; - SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode; - - return (mode_a > mode_b) - (mode_a < mode_b); -} - - -static int cmp_colors(const void *a, const void *b) { - t_color color_a = ((const struct sprite_primitive *)a)->color; - t_color color_b = ((const struct sprite_primitive *)b)->color; - - /* check reds */ - if (color_a.r < color_b.r) - return -1; - else if (color_a.r > color_b.r) - return 1; - - /* reds were equal, check greens */ - else if (color_a.g < color_b.g) - return -1; - else if (color_a.g > color_b.g) - return 1; - - /* greens were equal, check blues */ - else if (color_a.b < color_b.b) - return -1; - else if (color_a.b > color_b.b) - return 1; - - /* blues were equal, check alphas */ - else if (color_a.a < color_b.a) - return -1; - else if (color_a.a > color_b.a) - return 1; - - /* entirely equal */ - else - return 0; -} - - -static int cmp_layers(const void *a, const void *b) { - int layer_a = ((const struct sprite_primitive *)a)->layer; - int layer_b = ((const struct sprite_primitive *)b)->layer; - - return (layer_a > layer_b) - (layer_a < layer_b); -} - - -/* TODO: not that we're SDL free we need to implement batching ourselves */ -/* necessary to allow the renderer to draw in batches in the best case */ -static void sort_sprites(struct sprite_primitive *sprites) { - size_t sprites_len = arrlenu(sprites); - qsort(sprites, sprites_len, sizeof *sprites, cmp_atlases); - qsort(sprites, sprites_len, sizeof *sprites, cmp_blend_modes); - qsort(sprites, sprites_len, sizeof *sprites, cmp_colors); - qsort(sprites, sprites_len, sizeof *sprites, cmp_layers); -} - - static void upload_quad_vertices(t_frect rect) { /* client memory needs to be reachable on glDraw*, so */ static float vertices[6 * 2]; @@ -277,7 +222,7 @@ static void create_circle_geometry(t_fvec2 position, /* the angle (in radians) to rotate by on each iteration */ float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); - + vertices[0].position.x = (float)position.x; vertices[0].position.y = (float)position.y; vertices[0].color.r = color.r; @@ -366,25 +311,21 @@ static void render_circle(struct circle_primitive *circle) { } -static void render_sprites(void) { - sort_sprites(ctx.render_queue_sprites); +static void render_2d(void) { + for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { + struct primitive_2d *current = &ctx.render_queue_2d[i]; - for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) { - render_sprite(&ctx.render_queue_sprites[i]); - } -} - - -static void render_rectangles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_rectangles); ++i) { - render_rectangle(&ctx.render_queue_rectangles[i]); - } -} - - -static void render_circles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_circles); ++i) { - render_circle(&ctx.render_queue_circles[i]); + switch (current->type) { + case PRIMITIVE_2D_SPRITE: + render_sprite(¤t->sprite); + break; + case PRIMITIVE_2D_RECT: + render_rectangle(¤t->rect); + break; + case PRIMITIVE_2D_CIRCLE: + render_circle(¤t->circle); + break; + } } } @@ -516,7 +457,7 @@ void render(void) { glDepthFunc(GL_ALWAYS); /* fill depth buffer with ones, 2d view is always in front */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); @@ -525,10 +466,8 @@ void render(void) { glLoadIdentity(); glEnable(GL_TEXTURE_2D); - - render_sprites(); - render_rectangles(); - render_circles(); + + render_2d(); } { @@ -537,7 +476,7 @@ void render(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - + glEnable(GL_CULL_FACE); glDepthFunc(GL_LESS); glEnable(GL_BLEND); diff --git a/src/rendering.h b/src/rendering.h index 5f348e4..099e595 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -9,7 +9,6 @@ typedef struct push_sprite_args { char *path; - int layer; t_color color; double rotation; SDL_BlendMode blend_mode; -- 2.30.2 From b7edb8a02e5df5cc308f76e58a2c470acb702127 Mon Sep 17 00:00:00 2001 From: wanp Date: Mon, 15 Jul 2024 23:34:59 -0300 Subject: [PATCH 09/27] .gitignore: ignore clangd .cache dir --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 86abade..cfb2988 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vs/ .vscode/ .idea/ +.cache/ .build/ build/ out/ -- 2.30.2 From 765e6bb8a078f06bdc01984b3859c5b29496ddeb Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Fri, 19 Jul 2024 23:37:28 +0300 Subject: [PATCH 10/27] build: add -gc-sections for non-MSVC compilers --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dd55ad..064f445 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,10 @@ else() -fno-trapping-math -freciprocal-math) set(BUILD_FLAGS_RELEASE - -flto) + -flto + -Wl,-gc-sections + -fdata-sections + -ffunction-sections) set(BUILD_FLAGS_DEBUG -g3 -gdwarf -- 2.30.2 From 867dea195804a82015a8d4e73a39410eb4d681e8 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Fri, 19 Jul 2024 23:38:10 +0300 Subject: [PATCH 11/27] textures.c: remove by one offsetiing, use macro for validity checking --- src/textures.c | 25 +++++++++++++------------ src/textures.h | 5 +---- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/textures.c b/src/textures.c index e10ff1a..9042b35 100644 --- a/src/textures.c +++ b/src/textures.c @@ -307,12 +307,12 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path upload_texture_from_surface(new_texture.loner_texture, surface); new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; shput(cache->hash, path, new_texture); - return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; + return (t_texture_key){ (int)shgeti(cache->hash, path) }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; - return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; + return (t_texture_key){ (int)shgeti(cache->hash, path) }; } } @@ -370,13 +370,14 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { if (texture == -1) { return textures_load(cache, path); } else - return (t_texture_key){ (int)texture + 1 }; + return (t_texture_key){ (int)texture }; } +#endif /* generic implementation of textures_get_key() */ t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { - if (key.id != 0) { - return cache->hash[key.id - 1].value.srcrect; + if (m_texture_key_is_valid(key)) { + return cache->hash[key.id].value.srcrect; } else { CRY("Texture lookup failed.", "Tried to get texture that isn't loaded."); @@ -386,9 +387,9 @@ t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { - if (key.id != 0) { - if (cache->hash[key.id - 1].value.loner_texture != 0) - return cache->hash[key.id - 1].value.srcrect; + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture != 0) + return cache->hash[key.id].value.srcrect; else return (t_rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; } else { @@ -400,11 +401,11 @@ t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target) { - if (key.id != 0) { - if (cache->hash[key.id - 1].value.loner_texture == 0) - glBindTexture(target, cache->atlas_textures[cache->hash[key.id - 1].value.atlas_index]); + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture == 0) + glBindTexture(target, cache->atlas_textures[cache->hash[key.id].value.atlas_index]); else - glBindTexture(target, cache->hash[key.id - 1].value.loner_texture); + glBindTexture(target, cache->hash[key.id].value.loner_texture); } else if (key.id == 0) { CRY("Texture binding failed.", "Tried to get texture that isn't loaded."); diff --git a/src/textures.h b/src/textures.h index b244208..411d13f 100644 --- a/src/textures.h +++ b/src/textures.h @@ -11,10 +11,7 @@ typedef struct { int id; } t_texture_key; /* tests whether given key structure corresponds to any texture */ -#define m_texture_key_is_valid(p_key) ((p_key).id != 0) - -/* tests whether given key corresponds to atlas texture or a loner */ -#define m_texture_is_atlas(p_key) ((p_key).id > 0) +#define m_texture_key_is_valid(p_key) ((p_key).id != -1) void textures_cache_init(struct texture_cache *cache, SDL_Window *window); void textures_cache_deinit(struct texture_cache *cache); -- 2.30.2 From 875af2a758990a9d8d0664bc068a95c68f919cf7 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Fri, 19 Jul 2024 23:44:29 +0300 Subject: [PATCH 12/27] experimental: textures_get_key() caching for embedded in binary path strings --- src/textures.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/textures.c b/src/textures.c index 9042b35..bd766e3 100644 --- a/src/textures.c +++ b/src/textures.c @@ -361,7 +361,39 @@ void textures_update_atlas(struct texture_cache *cache) { arrfree(rects); } +/* EXPERIMANTAL: LIKELY TO BE REMOVED! */ +/* todo: If it's proven to be useful: add runtime checking for .rodata > .data */ +#ifdef __unix__ /* use rodata elf section for fast lookups of repeating textures */ +extern const char start_rodata_address[]; +extern const char stop_rodata_heuristic[]; + +asm(".set start_rodata_address, .rodata"); +asm(".set stop_rodata_heuristic, .data"); /* there's nothing in default linker script to know the size of .rodata */ + +t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { + static const char *last_path = NULL; + static t_texture_key last_texture; + + /* fast path */ + if (path == last_path && path >= start_rodata_address && path < stop_rodata_heuristic) + return last_texture; + + /* hash tables are assumed to be stable, so we just return indices */ + int texture = (int)shgeti(cache->hash, path); + + /* load it if it isn't */ + if (texture == -1) { + last_texture = textures_load(cache, path); + } else + last_texture = (t_texture_key){ texture }; + + last_path = path; + + return last_texture; +} + +#else t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { /* hash tables are assumed to be stable, so we just return indices */ ptrdiff_t texture = shgeti(cache->hash, path); -- 2.30.2 From 32b83d68acd6589b1c543d800f7479241ce820de Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 20 Jul 2024 00:48:30 +0300 Subject: [PATCH 13/27] demo: proper uvs! --- src/game/scenes/ingame.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 21bdec3..ad7d6f6 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -15,17 +15,17 @@ static void ingame_tick(struct state *state) { (t_fvec3){ -1, -1, 0 }, (t_fvec3){ 1, -1, 0 }, (t_fvec3){ 1, 1, 0 }, - (t_shvec2){ 0, 0 }, - (t_shvec2){ 2048, 0 }, - (t_shvec2){ 2048, 2048 }); + (t_shvec2){ 0, 2048 }, + (t_shvec2){ 2048, 2048 }, + (t_shvec2){ 2048, 0 }); unfurl_triangle("/assets/big-violet.png", (t_fvec3){ 1, 1, 0 }, (t_fvec3){ -1, 1, 0 }, (t_fvec3){ -1, -1, 0 }, - (t_shvec2){ 2048, 2048 }, - (t_shvec2){ 0, 2048 }, - (t_shvec2){ 0, 0 }); + (t_shvec2){ 2048, 0 }, + (t_shvec2){ 0, 0 }, + (t_shvec2){ 0, 2048 }); } -- 2.30.2 From dfde000a3a2252648d08f60812e88ae02eab4acf Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 15:10:19 +0300 Subject: [PATCH 14/27] rendering.c: batching for sprites (blended vs unblended), separation of rendering submodules; textures.c: textures_get_atlas_id() --- src/private/rendering.h | 26 +- src/rendering.c | 386 ++++------------------------ src/rendering.h | 4 +- src/rendering/circles.h | 132 ++++++++++ src/rendering/quad_element_buffer.h | 41 +++ src/rendering/sprites.h | 220 ++++++++++++++++ src/rendering/triangles.h | 117 +++++++++ src/textures.c | 32 ++- src/textures.h | 7 +- 9 files changed, 609 insertions(+), 356 deletions(-) create mode 100644 src/rendering/circles.h create mode 100644 src/rendering/quad_element_buffer.h create mode 100644 src/rendering/sprites.h create mode 100644 src/rendering/triangles.h diff --git a/src/private/rendering.h b/src/private/rendering.h index 73a9cc2..8257a6e 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -13,11 +13,33 @@ struct sprite_primitive { t_frect rect; t_color color; - double rotation; - SDL_BlendMode blend_mode; + float rotation; t_texture_key texture_key; bool flip_x; bool flip_y; + bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */ +}; + +/* interleaved vertex array data */ +/* TODO: use int16_t for uvs */ +/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ +struct sprite_primitive_payload { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + t_color c0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + t_color c1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + t_color c2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; + t_color c3; }; struct rect_primitive { diff --git a/src/rendering.c b/src/rendering.c index 17d88ae..01d4312 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -1,16 +1,17 @@ #include "private/rendering.h" +#include "rendering/sprites.h" +#include "rendering/triangles.h" +#include "rendering/circles.h" #include "context.h" #include "textures.h" #include -#include #include +#include #include -#include #include -/* http://www.swiftless.com/opengltuts.html */ void render_queue_clear(void) { /* since i don't intend to free the queues, */ @@ -23,53 +24,6 @@ void render_queue_clear(void) { } -/* - * an implementation note: - * try to avoid doing expensive work in the push functions, - * because they will be called multiple times in the main loop - * before anything is really rendered - */ -/* sprite */ -void push_sprite(char *path, t_frect rect) { - struct sprite_primitive sprite = { - .rect = rect, - .color = (t_color) { 255, 255, 255, 255 }, - .rotation = 0.0, - .blend_mode = SDL_BLENDMODE_BLEND, - .texture_key = textures_get_key(&ctx.texture_cache, path), - .flip_x = false, - .flip_y = false, - }; - - struct primitive_2d primitive = { - .type = PRIMITIVE_2D_SPRITE, - .sprite = sprite, - }; - - arrput(ctx.render_queue_2d, primitive); -} - - -void push_sprite_ex(t_frect rect, t_push_sprite_args args) { - struct sprite_primitive sprite = { - .rect = rect, - .color = args.color, - .rotation = args.rotation, - .blend_mode = args.blend_mode, - .texture_key = textures_get_key(&ctx.texture_cache, args.path), - .flip_x = args.flip_x, - .flip_y = args.flip_y, - }; - - struct primitive_2d primitive = { - .type = PRIMITIVE_2D_SPRITE, - .sprite = sprite, - }; - - arrput(ctx.render_queue_2d, primitive); -} - - /* rectangle */ void push_rectangle(t_frect rect, t_color color) { struct rect_primitive rectangle = { @@ -86,58 +40,6 @@ void push_rectangle(t_frect rect, t_color color) { } -/* circle */ -void push_circle(t_fvec2 position, float radius, t_color color) { - struct circle_primitive circle = { - .radius = radius, - .color = color, - .position = position, - }; - - struct primitive_2d primitive = { - .type = PRIMITIVE_2D_CIRCLE, - .circle = circle, - }; - - arrput(ctx.render_queue_2d, primitive); -} - - -/* TODO: automatic handling of repeating textures */ -/* for that we could allocate a loner texture */ -void unfurl_triangle(const char *path, - t_fvec3 v0, - t_fvec3 v1, - t_fvec3 v2, - t_shvec2 uv0, - t_shvec2 uv1, - t_shvec2 uv2) -{ - const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); - - struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); - if (!batch_p) { - struct mesh_batch item = {0}; - hmput(ctx.uncolored_mesh_batches, texture_key, item); - batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ - } - - union uncolored_space_triangle triangle = { .primitive = { - .v0 = v0, - .v1 = v1, - .v2 = v2, - .uv1 = m_to_fvec2(uv1), - .uv0 = m_to_fvec2(uv0), - .uv2 = m_to_fvec2(uv2), - }}; - - union uncolored_space_triangle *triangles = - (union uncolored_space_triangle *)batch_p->value.primitives; - arrpush(triangles, triangle); - batch_p->value.primitives = (uint8_t *)triangles; -} - - static void upload_quad_vertices(t_frect rect) { /* client memory needs to be reachable on glDraw*, so */ static float vertices[6 * 2]; @@ -153,51 +55,7 @@ static void upload_quad_vertices(t_frect rect) { } -/* TODO: texture flipping */ -/* assumes that orthogonal matrix setup is done already */ -static void render_sprite(struct sprite_primitive *sprite) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glClientActiveTexture(GL_TEXTURE0); - - textures_bind(&ctx.texture_cache, sprite->texture_key, GL_TEXTURE_2D); - - t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->texture_key); - t_rect dims = textures_get_dims(&ctx.texture_cache, sprite->texture_key); - - glTexCoordPointer(2, - GL_FLOAT, - 0, - /* TODO: try using shorts */ - (void *)(float[6 * 2]) { - (float)srcrect.x / (float)dims.w, - (float)srcrect.y / (float)dims.h, - (float)srcrect.x / (float)dims.w, - (float)(srcrect.y + srcrect.h) / (float)dims.h, - (float)(srcrect.x + srcrect.w) / (float)dims.w, - (float)(srcrect.y + srcrect.h) / (float)dims.h, - (float)(srcrect.x + srcrect.w) / (float)dims.w, - (float)(srcrect.y + srcrect.h) / (float)dims.h, - (float)(srcrect.x + srcrect.w) / (float)dims.w, - (float)srcrect.y / (float)dims.h, - (float)srcrect.x / (float)dims.w, - (float)srcrect.y / (float)dims.h }); - - glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a); - - // SDL_SetTextureBlendMode(texture, sprite->blend_mode); - - glEnableClientState(GL_VERTEX_ARRAY); - upload_quad_vertices(sprite->rect); - - glDrawArrays(GL_TRIANGLES, 0, 6); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, 0); -} - - -static void render_rectangle(struct rect_primitive *rectangle) { +static void render_rectangle(const struct rect_primitive *rectangle) { glColor4ub(rectangle->color.r, rectangle->color.g, rectangle->color.b, rectangle->color.a); @@ -209,116 +67,42 @@ static void render_rectangle(struct rect_primitive *rectangle) { } -/* vertices_out and indices_out MUST BE FREED */ -static void create_circle_geometry(t_fvec2 position, - t_color color, - float radius, - size_t num_vertices, - SDL_Vertex **vertices_out, - int **indices_out) -{ - SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1)); - int *indices = cmalloc(sizeof *indices * (num_vertices * 3)); - - /* the angle (in radians) to rotate by on each iteration */ - float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); - - vertices[0].position.x = (float)position.x; - vertices[0].position.y = (float)position.y; - vertices[0].color.r = color.r; - vertices[0].color.g = color.g; - vertices[0].color.b = color.b; - vertices[0].color.a = color.a; - vertices[0].tex_coord = (SDL_FPoint){ 0, 0 }; - - /* this point will rotate around the center */ - float start_x = 0.0f - radius; - float start_y = 0.0f; - - for (size_t i = 1; i < num_vertices + 1; ++i) { - float final_seg_rotation_angle = (float)i * seg_rotation_angle; - - vertices[i].position.x = - cos(final_seg_rotation_angle) * start_x - - sin(final_seg_rotation_angle) * start_y; - vertices[i].position.y = - cos(final_seg_rotation_angle) * start_y + - sin(final_seg_rotation_angle) * start_x; - - vertices[i].position.x += position.x; - vertices[i].position.y += position.y; - - vertices[i].color.r = color.r; - vertices[i].color.g = color.g; - vertices[i].color.b = color.b; - vertices[i].color.a = color.a; - - vertices[i].tex_coord = (SDL_FPoint){ 0, 0 }; - - - size_t triangle_offset = 3 * (i - 1); - - /* center point index */ - indices[triangle_offset] = 0; - /* generated point index */ - indices[triangle_offset + 1] = (int)i; - - size_t index = (i + 1) % num_vertices; - if (index == 0) - index = num_vertices; - indices[triangle_offset + 2] = (int)index; - } - - *vertices_out = vertices; - *indices_out = indices; -} - - -static void render_circle(struct circle_primitive *circle) { - SDL_Vertex *vertices = NULL; - int *indices = NULL; - int num_vertices = (int)circle->radius; - - create_circle_geometry(circle->position, - circle->color, - circle->radius, - num_vertices, - &vertices, - &indices); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, - GL_FLOAT, - sizeof (SDL_Vertex), - &vertices->position); - - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, - GL_UNSIGNED_BYTE, - sizeof (SDL_Vertex), - &vertices->color); - - glDrawElements(GL_TRIANGLES, - num_vertices * 3, - GL_UNSIGNED_INT, - indices); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - free(vertices); - free(indices); -} - - static void render_2d(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { - struct primitive_2d *current = &ctx.render_queue_2d[i]; + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glDisable(GL_CULL_FACE); + glDepthFunc(GL_LESS); + + const size_t render_queue_len = arrlenu(ctx.render_queue_2d); + + size_t batch_count = 0; + + for (size_t i = 0; i < render_queue_len; ++i) { + const struct primitive_2d *current = &ctx.render_queue_2d[i]; switch (current->type) { - case PRIMITIVE_2D_SPRITE: - render_sprite(¤t->sprite); + case PRIMITIVE_2D_SPRITE: { + const struct sprite_batch batch = + collect_sprite_batch(current, render_queue_len - i); + + if (batch.blend) { + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + render_sprites(current, batch.size, false); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + glDepthRange((double)batch_count / 65536, 1.0); + glDisable(GL_BLEND); + + render_sprites(current, batch.size, true); + } + + i += batch.size - 1; ++batch_count; break; + } case PRIMITIVE_2D_RECT: render_rectangle(¤t->rect); break; @@ -330,80 +114,13 @@ static void render_2d(void) { } -static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, - t_texture_key texture_key) -{ - size_t primitives_len = arrlenu(batch->primitives); - - if (primitives_len == 0) - return; - - /* create vertex array object */ - if (batch->buffer == 0) - glGenBuffers(1, &batch->buffer); - - /* TODO: try using mapped buffers while building batches instead? */ - /* this way we could skip client side copy that is kept until commitment */ - /* alternatively we could commit glBufferSubData based on a threshold */ - - /* update pixel-based uvs to correspond with texture atlases */ - for (size_t i = 0; i < primitives_len; ++i) { - struct uncolored_space_triangle_payload *payload = - &((union uncolored_space_triangle *)batch->primitives)[i].payload; - - t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); - t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); - - const float wr = (float)srcrect.w / (float)dims.w; - const float hr = (float)srcrect.h / (float)dims.h; - const float xr = (float)srcrect.x / (float)dims.w; - const float yr = (float)srcrect.y / (float)dims.h; - - payload->uv0.x = xr + ((float)payload->uv0.x / (float)srcrect.w) * wr; - payload->uv0.y = yr + ((float)payload->uv0.y / (float)srcrect.h) * hr; - payload->uv1.x = xr + ((float)payload->uv1.x / (float)srcrect.w) * wr; - payload->uv1.y = yr + ((float)payload->uv1.y / (float)srcrect.h) * hr; - payload->uv2.x = xr + ((float)payload->uv2.x / (float)srcrect.w) * wr; - payload->uv2.y = yr + ((float)payload->uv2.y / (float)srcrect.h) * hr; - } - - textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D); - - glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); - - /* upload batched data */ - glBufferData(GL_ARRAY_BUFFER, - primitives_len * sizeof (struct uncolored_space_triangle_payload), - batch->primitives, - GL_STREAM_DRAW); - - /* vertex specification*/ - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, - GL_FLOAT, - offsetof(struct uncolored_space_triangle_payload, v1), - (void *)offsetof(struct uncolored_space_triangle_payload, v0)); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glClientActiveTexture(GL_TEXTURE0); - glTexCoordPointer(2, - GL_FLOAT, - offsetof(struct uncolored_space_triangle_payload, v1), - (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); - - /* commit for drawing */ - glDrawArrays(GL_TRIANGLES, 0, 3 * (int)primitives_len); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - /* invalidate the buffer immediately */ - glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - - static void render_space(void) { + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); @@ -420,6 +137,7 @@ static void render_space(void) { void render(void) { textures_update_atlas(&ctx.texture_cache); + /* TODO: only do this when needed */ /* fit rendering context onto the resizable screen */ if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT; @@ -441,9 +159,6 @@ void render(void) { ); } - glEnable(GL_DEPTH_TEST); - glEnable(GL_ALPHA_TEST); - glClearColor((1.0f / 255) * 230, (1.0f / 255) * 230, (1.0f / 255) * 230, 1); @@ -453,11 +168,6 @@ void render(void) { GL_STENCIL_BUFFER_BIT); { - glDisable(GL_CULL_FACE); - glDepthFunc(GL_ALWAYS); /* fill depth buffer with ones, 2d view is always in front */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); @@ -465,8 +175,6 @@ void render(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glEnable(GL_TEXTURE_2D); - render_2d(); } @@ -477,12 +185,6 @@ void render(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glEnable(GL_CULL_FACE); - glDepthFunc(GL_LESS); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - /* TODO: use depth test to optimize gui regions away */ render_space(); } diff --git a/src/rendering.h b/src/rendering.h index 099e595..7e48cd4 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -10,10 +10,10 @@ typedef struct push_sprite_args { char *path; t_color color; - double rotation; - SDL_BlendMode blend_mode; + float rotation; bool flip_x; bool flip_y; + bool blend; } t_push_sprite_args; /* clears all render queues */ diff --git a/src/rendering/circles.h b/src/rendering/circles.h new file mode 100644 index 0000000..d7d62c9 --- /dev/null +++ b/src/rendering/circles.h @@ -0,0 +1,132 @@ +/* a rendering.c mixin */ +#ifndef CIRCLES_H +#define CIRCLES_H + +#include "../util.h" +#include "../private/rendering.h" +#include "../context.h" + +#include +#include + +#include + + +void push_circle(t_fvec2 position, float radius, t_color color) { + struct circle_primitive circle = { + .radius = radius, + .color = color, + .position = position, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_CIRCLE, + .circle = circle, + }; + + arrput(ctx.render_queue_2d, primitive); +} + +/* TODO: caching and reuse scheme */ +/* vertices_out and indices_out MUST BE FREED */ +static void create_circle_geometry(t_fvec2 position, + t_color color, + float radius, + size_t num_vertices, + SDL_Vertex **vertices_out, + int **indices_out) +{ + SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1)); + int *indices = cmalloc(sizeof *indices * (num_vertices * 3)); + + /* the angle (in radians) to rotate by on each iteration */ + float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); + + vertices[0].position.x = (float)position.x; + vertices[0].position.y = (float)position.y; + vertices[0].color.r = color.r; + vertices[0].color.g = color.g; + vertices[0].color.b = color.b; + vertices[0].color.a = color.a; + vertices[0].tex_coord = (SDL_FPoint){ 0, 0 }; + + /* this point will rotate around the center */ + float start_x = 0.0f - radius; + float start_y = 0.0f; + + for (size_t i = 1; i < num_vertices + 1; ++i) { + float final_seg_rotation_angle = (float)i * seg_rotation_angle; + + vertices[i].position.x = + cosf(final_seg_rotation_angle) * start_x - + sinf(final_seg_rotation_angle) * start_y; + vertices[i].position.y = + cosf(final_seg_rotation_angle) * start_y + + sinf(final_seg_rotation_angle) * start_x; + + vertices[i].position.x += position.x; + vertices[i].position.y += position.y; + + vertices[i].color.r = color.r; + vertices[i].color.g = color.g; + vertices[i].color.b = color.b; + vertices[i].color.a = color.a; + + vertices[i].tex_coord = (SDL_FPoint){ 0, 0 }; + + + size_t triangle_offset = 3 * (i - 1); + + /* center point index */ + indices[triangle_offset] = 0; + /* generated point index */ + indices[triangle_offset + 1] = (int)i; + + size_t index = (i + 1) % num_vertices; + if (index == 0) + index = num_vertices; + indices[triangle_offset + 2] = (int)index; + } + + *vertices_out = vertices; + *indices_out = indices; +} + + +static void render_circle(const struct circle_primitive *circle) { + SDL_Vertex *vertices = NULL; + int *indices = NULL; + int num_vertices = (int)circle->radius; + + create_circle_geometry(circle->position, + circle->color, + circle->radius, + num_vertices, + &vertices, + &indices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, + GL_FLOAT, + sizeof (SDL_Vertex), + &vertices->position); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + sizeof (SDL_Vertex), + &vertices->color); + + glDrawElements(GL_TRIANGLES, + num_vertices * 3, + GL_UNSIGNED_INT, + indices); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + free(vertices); + free(indices); +} + +#endif diff --git a/src/rendering/quad_element_buffer.h b/src/rendering/quad_element_buffer.h new file mode 100644 index 0000000..4f832ee --- /dev/null +++ b/src/rendering/quad_element_buffer.h @@ -0,0 +1,41 @@ +/* a rendering.c mixin */ +#ifndef QUAD_ELEMENT_BUFFER_H +#define QUAD_ELEMENT_BUFFER_H + +#include + +#include + +#define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) + +static void bind_quad_element_buffer(void) { + static GLuint buffer = 0; + + /* it's only generated once at runtime */ + if (buffer == 0) { + glGenBuffers(1, &buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + QUAD_ELEMENT_BUFFER_LENGTH * 6, + NULL, + GL_STATIC_DRAW); + + uint16_t *const indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, + GL_WRITE_ONLY); + + for (uint16_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { + indices[i * 6 + 0] = (uint16_t)(i * 4 + 0); + indices[i * 6 + 1] = (uint16_t)(i * 4 + 1); + indices[i * 6 + 2] = (uint16_t)(i * 4 + 2); + indices[i * 6 + 3] = (uint16_t)(i * 4 + 2); + indices[i * 6 + 4] = (uint16_t)(i * 4 + 3); + indices[i * 6 + 5] = (uint16_t)(i * 4 + 0); + } + + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + } else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); +} + +#endif diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h new file mode 100644 index 0000000..3340950 --- /dev/null +++ b/src/rendering/sprites.h @@ -0,0 +1,220 @@ +/* a rendering.c mixin */ +#ifndef SPRITES_H +#define SPRITES_H + +#include "../textures.h" +#include "../rendering.h" +#include "../context.h" +#include "quad_element_buffer.h" + +#include + +#include +#include + +/* + * an implementation note: + * try to avoid doing expensive work in the push functions, + * because they will be called multiple times in the main loop + * before anything is really rendered + */ +/* sprite */ +void push_sprite(char *path, t_frect rect) { + struct sprite_primitive sprite = { + .rect = rect, + .color = (t_color) { 255, 255, 255, 255 }, + .rotation = 0.0, + .texture_key = textures_get_key(&ctx.texture_cache, path), + .flip_x = false, + .flip_y = false, + .blend = true, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); +} + + +void push_sprite_ex(t_frect rect, t_push_sprite_args args) { + struct sprite_primitive sprite = { + .rect = rect, + .color = args.color, + .rotation = args.rotation, + .texture_key = textures_get_key(&ctx.texture_cache, args.path), + .flip_x = args.flip_x, + .flip_y = args.flip_y, + .blend = args.blend, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); +} + + +static struct sprite_batch { + int atlas_id; + size_t size; /* how many primitives are in current batch */ + bool blend; /* whether it's blended or not */ +} collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { + /* assumes that first primitive is already a sprite */ + struct sprite_batch result = { + .atlas_id = + textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key), + .blend = primitives[0].sprite.blend, + }; + + /* batch size is clamped so that reallocated short indices could be used */ + if (len >= QUAD_ELEMENT_BUFFER_LENGTH) + len = QUAD_ELEMENT_BUFFER_LENGTH; + + for (size_t i = 0; i < len; ++i) { + const struct primitive_2d *const current = &primitives[i]; + + /* don't touch things other than sprites */ + if (current->type != PRIMITIVE_2D_SPRITE) + break; + + /* only collect the same blend modes */ + if (current->sprite.blend != result.blend) + break; + + /* only collect the same texture atlases */ + if (textures_get_atlas_id(&ctx.texture_cache, current->sprite.texture_key) + != result.atlas_id) + break; + + ++result.size; + } + + return result; +} + + +/* TODO: texture flipping */ +/* assumes that orthogonal matrix setup is done already */ +static void render_sprites(const struct primitive_2d primitives[], + const size_t len, + const bool reversed) +{ + /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ + static GLuint vertex_array = 0; + if (vertex_array == 0) + glGenBuffers(1, &vertex_array); + + glBindBuffer(GL_ARRAY_BUFFER, vertex_array); + glBufferData(GL_ARRAY_BUFFER, + sizeof (struct sprite_primitive_payload) * len, + NULL, + GL_STREAM_DRAW); + + const t_rect srcrect = + textures_get_srcrect(&ctx.texture_cache, primitives->sprite.texture_key); + const t_rect dims = + textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key); + + /* vertex population over a mapped buffer */ + { + /* TODO: check errors, ensure alignment ? */ + struct sprite_primitive_payload *const payload = + glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + + for (size_t i = 0; i < len; ++i) { + const size_t cur = reversed ? len - i - 1: i; + const struct sprite_primitive sprite = primitives[cur].sprite; + + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; + + payload[i] = (struct sprite_primitive_payload) { + /* upper-left */ + .v0 = { + sprite.rect.x, + sprite.rect.y }, + .uv0 = { + xr, + yr, }, + + /* bottom-left */ + .v1 = { + (sprite.rect.x), + (sprite.rect.y + sprite.rect.h) }, + .uv1 = { + xr, + yr + hr, }, + + /* bottom-right */ + .v2 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y + sprite.rect.h) }, + .uv2 = { + xr + wr, + yr + hr, }, + + /* upper-right */ + .v3 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y) }, + .uv3 = { + xr + wr, + yr, }, + + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + } + + glUnmapBuffer(GL_ARRAY_BUFFER); + } + + /* vertex specification */ + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, + GL_FLOAT, + offsetof(struct sprite_primitive_payload, v1), + (void *)offsetof(struct sprite_primitive_payload, v0)); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, + GL_FLOAT, + offsetof(struct sprite_primitive_payload, v1), + (void *)offsetof(struct sprite_primitive_payload, uv0)); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + offsetof(struct sprite_primitive_payload, v1), + (void *)offsetof(struct sprite_primitive_payload, c0)); + + textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D); + + bind_quad_element_buffer(); + + glDrawElements(GL_TRIANGLES, 6 * (GLsizei)len, GL_UNSIGNED_SHORT, NULL); + + /* clear the state */ + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +#endif diff --git a/src/rendering/triangles.h b/src/rendering/triangles.h new file mode 100644 index 0000000..8e88b11 --- /dev/null +++ b/src/rendering/triangles.h @@ -0,0 +1,117 @@ +/* a rendering.c mixin */ +#ifndef TRIANGLES_H +#define TRIANGLES_H + +#include "../textures.h" +#include "../context.h" + +#include + +/* TODO: automatic handling of repeating textures */ +/* for that we could allocate a loner texture */ +void unfurl_triangle(const char *path, + t_fvec3 v0, + t_fvec3 v1, + t_fvec3 v2, + t_shvec2 uv0, + t_shvec2 uv1, + t_shvec2 uv2) +{ + const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); + + struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); + if (!batch_p) { + struct mesh_batch item = {0}; + hmput(ctx.uncolored_mesh_batches, texture_key, item); + batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ + } + + union uncolored_space_triangle triangle = { .primitive = { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .uv1 = m_to_fvec2(uv1), + .uv0 = m_to_fvec2(uv0), + .uv2 = m_to_fvec2(uv2), + }}; + + union uncolored_space_triangle *triangles = + (union uncolored_space_triangle *)batch_p->value.primitives; + arrpush(triangles, triangle); + batch_p->value.primitives = (uint8_t *)triangles; +} + + +static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, + t_texture_key texture_key) +{ + size_t primitives_len = arrlenu(batch->primitives); + + if (primitives_len == 0) + return; + + /* create vertex array object */ + if (batch->buffer == 0) + glGenBuffers(1, &batch->buffer); + + /* TODO: try using mapped buffers while building batches instead? */ + /* this way we could skip client side copy that is kept until commitment */ + /* alternatively we could commit glBufferSubData based on a threshold */ + + /* update pixel-based uvs to correspond with texture atlases */ + for (size_t i = 0; i < primitives_len; ++i) { + struct uncolored_space_triangle_payload *payload = + &((union uncolored_space_triangle *)batch->primitives)[i].payload; + + t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); + t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); + + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; + + payload->uv0.x = xr + ((float)payload->uv0.x / (float)srcrect.w) * wr; + payload->uv0.y = yr + ((float)payload->uv0.y / (float)srcrect.h) * hr; + payload->uv1.x = xr + ((float)payload->uv1.x / (float)srcrect.w) * wr; + payload->uv1.y = yr + ((float)payload->uv1.y / (float)srcrect.h) * hr; + payload->uv2.x = xr + ((float)payload->uv2.x / (float)srcrect.w) * wr; + payload->uv2.y = yr + ((float)payload->uv2.y / (float)srcrect.h) * hr; + } + + textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D); + + glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); + + /* upload batched data */ + glBufferData(GL_ARRAY_BUFFER, + primitives_len * sizeof (struct uncolored_space_triangle_payload), + batch->primitives, + GL_STREAM_DRAW); + + /* vertex specification*/ + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, + GL_FLOAT, + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, v0)); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, + GL_FLOAT, + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); + + /* commit for drawing */ + glDrawArrays(GL_TRIANGLES, 0, 3 * (int)primitives_len); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + /* invalidate the buffer immediately */ + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#endif diff --git a/src/textures.c b/src/textures.c index bd766e3..d912581 100644 --- a/src/textures.c +++ b/src/textures.c @@ -307,12 +307,12 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path upload_texture_from_surface(new_texture.loner_texture, surface); new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; shput(cache->hash, path, new_texture); - return (t_texture_key){ (int)shgeti(cache->hash, path) }; + return (t_texture_key){ shgeti(cache->hash, path) }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; - return (t_texture_key){ (int)shgeti(cache->hash, path) }; + return (t_texture_key){ shgeti(cache->hash, path) }; } } @@ -357,7 +357,7 @@ void textures_update_atlas(struct texture_cache *cache) { recreate_current_atlas_texture(cache); cache->is_dirty = false; - + arrfree(rects); } @@ -371,24 +371,27 @@ extern const char stop_rodata_heuristic[]; asm(".set start_rodata_address, .rodata"); asm(".set stop_rodata_heuristic, .data"); /* there's nothing in default linker script to know the size of .rodata */ +/* TODO: it might be better to contruct a new table that hashes pointers, not strings, to texture keys */ +/* this way every used texture will benefit, no matter the order of commission */ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { static const char *last_path = NULL; static t_texture_key last_texture; /* fast path */ - if (path == last_path && path >= start_rodata_address && path < stop_rodata_heuristic) + if (path == last_path) return last_texture; /* hash tables are assumed to be stable, so we just return indices */ - int texture = (int)shgeti(cache->hash, path); + ptrdiff_t texture = shgeti(cache->hash, path); /* load it if it isn't */ if (texture == -1) { last_texture = textures_load(cache, path); } else - last_texture = (t_texture_key){ texture }; + last_texture = (t_texture_key){ (uint16_t)texture }; - last_path = path; + if (path >= start_rodata_address && path < stop_rodata_heuristic) + last_path = path; return last_texture; } @@ -402,11 +405,24 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { if (texture == -1) { return textures_load(cache, path); } else - return (t_texture_key){ (int)texture }; + return (t_texture_key){ (uint16_t)texture }; } #endif /* generic implementation of textures_get_key() */ +int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key) { + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture != 0) + return -cache->hash[key.id].value.loner_texture; + else + return cache->hash[key.id].value.atlas_index; + } else { + CRY("Texture lookup failed.", + "Tried to get atlas id that isn't loaded."); + return 0; + } +} + t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { if (m_texture_key_is_valid(key)) { return cache->hash[key.id].value.srcrect; diff --git a/src/textures.h b/src/textures.h index 411d13f..bf9bf8d 100644 --- a/src/textures.h +++ b/src/textures.h @@ -8,10 +8,10 @@ #include /* type safe structure for persistent texture handles */ -typedef struct { int id; } t_texture_key; +typedef struct { uint16_t id; } t_texture_key; /* tests whether given key structure corresponds to any texture */ -#define m_texture_key_is_valid(p_key) ((p_key).id != -1) +#define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) void textures_cache_init(struct texture_cache *cache, SDL_Window *window); void textures_cache_deinit(struct texture_cache *cache); @@ -39,6 +39,9 @@ t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key /* returns a rect of dimensions of the whole texture (whole atlas) */ t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key); +/* returns an identifier that is equal for all textures placed in the same atlas */ +int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key); + /* binds atlas texture in opengl state */ void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); -- 2.30.2 From b5c8f7100a7f702c8cde7dfb3436b3ab8a617981 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 15:12:17 +0300 Subject: [PATCH 15/27] -fsanitize=undefined --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 064f445..79e9a8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ else() set(BUILD_FLAGS_DEBUG -g3 -gdwarf + -fsanitize=undefined -fsanitize-trap=undefined) target_compile_options(${PROJECT_NAME} PRIVATE ${WARNING_FLAGS} -- 2.30.2 From c529e6ee4e7c36b2d3f6913af832d1d49e079534 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 15:33:48 +0300 Subject: [PATCH 16/27] rendering.c: correct-er order and settings of of 2d and 3d --- src/main.c | 6 ++++-- src/rendering.c | 31 +++++++++++++++++-------------- src/rendering/sprites.h | 3 ++- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main.c b/src/main.c index 33aae14..910c040 100644 --- a/src/main.c +++ b/src/main.c @@ -165,11 +165,12 @@ static bool initialize(void) { return false; } - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); /* init got far enough to create a window */ ctx.window = SDL_CreateWindow("emerald", @@ -192,6 +193,7 @@ static bool initialize(void) { } SDL_GL_MakeCurrent(ctx.window, ctx.gl_context); + SDL_GL_SetSwapInterval(1); int glad_status = gladLoadGL(); if (glad_status == 0) { diff --git a/src/rendering.c b/src/rendering.c index 01d4312..1acecc2 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -86,16 +86,19 @@ static void render_2d(void) { collect_sprite_batch(current, render_queue_len - i); if (batch.blend) { - glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDisable(GL_ALPHA_TEST); render_sprites(current, batch.size, false); } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_DEPTH_TEST); - glDepthRange((double)batch_count / 65536, 1.0); glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthRange((double)batch_count / UINT16_MAX, 1.0); + glDisable(GL_ALPHA_TEST); render_sprites(current, batch.size, true); } @@ -167,6 +170,16 @@ void render(void) { GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + render_space(); + } + { glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -178,15 +191,5 @@ void render(void) { render_2d(); } - { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - render_space(); - } - SDL_GL_SwapWindow(ctx.window); } diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 3340950..0cfef9a 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -18,7 +18,8 @@ * because they will be called multiple times in the main loop * before anything is really rendered */ -/* sprite */ +/* TODO: it might make sense to infer alpha channel presence / meaningfulness for textures in atlas */ +/* so that they are rendered with no blend / batched in a way to reduce overdraw automatically */ void push_sprite(char *path, t_frect rect) { struct sprite_primitive sprite = { .rect = rect, -- 2.30.2 From 687e42ddfdc8c0dac2664a22af69073853a0182c Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 15:44:34 +0300 Subject: [PATCH 17/27] rendering.c: sprite texture flipping --- src/rendering/sprites.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 0cfef9a..3995fb1 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -99,7 +99,6 @@ static struct sprite_batch { } -/* TODO: texture flipping */ /* assumes that orthogonal matrix setup is done already */ static void render_sprites(const struct primitive_2d primitives[], const size_t len, @@ -142,32 +141,32 @@ static void render_sprites(const struct primitive_2d primitives[], sprite.rect.x, sprite.rect.y }, .uv0 = { - xr, - yr, }, + xr + wr * sprite.flip_x, + yr + hr * sprite.flip_y, }, /* bottom-left */ .v1 = { (sprite.rect.x), (sprite.rect.y + sprite.rect.h) }, .uv1 = { - xr, - yr + hr, }, + xr + wr * sprite.flip_x, + yr + hr * !sprite.flip_y, }, /* bottom-right */ .v2 = { (sprite.rect.x + sprite.rect.w), (sprite.rect.y + sprite.rect.h) }, .uv2 = { - xr + wr, - yr + hr, }, + xr + wr * !sprite.flip_x, + yr + hr * !sprite.flip_y, }, /* upper-right */ .v3 = { (sprite.rect.x + sprite.rect.w), (sprite.rect.y) }, .uv3 = { - xr + wr, - yr, }, + xr + wr * !sprite.flip_x, + yr + hr * sprite.flip_y, }, /* equal for all (flat shaded) */ .c0 = sprite.color, -- 2.30.2 From 79bc261ccdc169ca69a3b6807a1a07c9da6da7e0 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 16:55:38 +0300 Subject: [PATCH 18/27] rendering.c: sprite rotation --- src/game/scenes/ingame.c | 17 ++++++ src/rendering.c | 1 + src/rendering/sprites.h | 120 +++++++++++++++++++++++++++------------ src/util.c | 8 +++ src/util.h | 47 +++++++-------- 5 files changed, 135 insertions(+), 58 deletions(-) diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index ad7d6f6..c4de337 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,6 +11,23 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); + push_sprite_ex((t_frect){ .x = 32, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 64, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .rotation = (float)M_PI / 64, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 96, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .blend = true }); + unfurl_triangle("/assets/big-violet.png", (t_fvec3){ -1, -1, 0 }, (t_fvec3){ 1, -1, 0 }, diff --git a/src/rendering.c b/src/rendering.c index 1acecc2..a5d34cd 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -90,6 +90,7 @@ static void render_2d(void) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); + glDepthRange((double)batch_count / UINT16_MAX, 1.0); glDisable(GL_ALPHA_TEST); render_sprites(current, batch.size, false); diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 3995fb1..6c78b5b 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -135,45 +135,95 @@ static void render_sprites(const struct primitive_2d primitives[], const float xr = (float)srcrect.x / (float)dims.w; const float yr = (float)srcrect.y / (float)dims.h; - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - sprite.rect.x, - sprite.rect.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* non-rotated case */ + if (sprite.rotation == 0.0f) { + payload[i] = (struct sprite_primitive_payload) { + /* upper-left */ + .v0 = { + sprite.rect.x, + sprite.rect.y }, + .uv0 = { + xr + wr * sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* bottom-left */ - .v1 = { - (sprite.rect.x), - (sprite.rect.y + sprite.rect.h) }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-left */ + .v1 = { + (sprite.rect.x), + (sprite.rect.y + sprite.rect.h) }, + .uv1 = { + xr + wr * sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* bottom-right */ - .v2 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y + sprite.rect.h) }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-right */ + .v2 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y + sprite.rect.h) }, + .uv2 = { + xr + wr * !sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* upper-right */ - .v3 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y) }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* upper-right */ + .v3 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y) }, + .uv3 = { + xr + wr * !sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* equal for all (flat shaded) */ - .c0 = sprite.color, - .c1 = sprite.color, - .c2 = sprite.color, - .c3 = sprite.color, - }; + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + } else { + /* rotated case */ + const t_fvec2 c = frect_center(sprite.rect); + const t_fvec2 d = { + .x = (cosf(sprite.rotation + (float)M_PI_4) * sprite.rect.w) * (float)M_SQRT1_2, + .y = (sinf(sprite.rotation + (float)M_PI_4) * sprite.rect.h) * (float)M_SQRT1_2, + }; + + payload[i] = (struct sprite_primitive_payload) { + /* upper-left */ + .v0 = { + c.x - d.x, + c.y - d.y }, + .uv0 = { + xr + wr * sprite.flip_x, + yr + hr * sprite.flip_y, }, + + /* bottom-left */ + .v1 = { + c.x - d.y, + c.y + d.x }, + .uv1 = { + xr + wr * sprite.flip_x, + yr + hr * !sprite.flip_y, }, + + /* bottom-right */ + .v2 = { + c.x + d.x, + c.y + d.y }, + .uv2 = { + xr + wr * !sprite.flip_x, + yr + hr * !sprite.flip_y, }, + + /* upper-right */ + .v3 = { + c.x + d.y, + c.y - d.x }, + .uv3 = { + xr + wr * !sprite.flip_x, + yr + hr * sprite.flip_y, }, + + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + } } glUnmapBuffer(GL_ARRAY_BUFFER); diff --git a/src/util.c b/src/util.c index 9e49a64..b368957 100644 --- a/src/util.c +++ b/src/util.c @@ -186,6 +186,14 @@ t_frect to_frect(t_rect rect) { } +t_fvec2 frect_center(t_frect rect) { + return (t_fvec2){ + .x = rect.x + rect.w / 2, + .y = rect.y + rect.h / 2, + }; +} + + t_fvec2 fvec2_from_vec2(t_vec2 vec) { return (t_fvec2) { .x = (float)vec.x, diff --git a/src/util.h b/src/util.h index 03be517..98b2fae 100644 --- a/src/util.h +++ b/src/util.h @@ -77,29 +77,6 @@ typedef struct color { } t_color; -/* a rectangle with the origin at the upper left (integer) */ -typedef struct rect { - int x, y; - int w, h; -} t_rect; - - -bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); - - -/* a rectangle with the origin at the upper left (floating point) */ -typedef struct frect { - float x, y; - float w, h; -} t_frect; - - -bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); - -/* TODO: generics and specials (see m_to_fvec2() for an example)*/ -t_frect to_frect(t_rect rect); - - /* a point in some space (integer) */ typedef struct vec2 { int x, y; @@ -125,6 +102,30 @@ typedef struct shvec2 { } t_shvec2; +/* a rectangle with the origin at the upper left (integer) */ +typedef struct rect { + int x, y; + int w, h; +} t_rect; + + +/* a rectangle with the origin at the upper left (floating point) */ +typedef struct frect { + float x, y; + float w, h; +} t_frect; + + +bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); + +bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); + +/* TODO: generics and specials (see m_to_fvec2() for an example)*/ +t_frect to_frect(t_rect rect); + +t_fvec2 frect_center(t_frect rect); + + /* aren't macros to prevent double evaluation with side effects */ /* maybe could be inlined? i hope LTO will resolve this */ t_fvec2 fvec2_from_vec2(t_vec2 vec); -- 2.30.2 From f5ba4a75bd6ce9ceb921b034796f109b13004a64 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 17:10:08 +0300 Subject: [PATCH 19/27] rendering.c: only update opengl viewport once it's actually changed --- src/context.h | 2 ++ src/input.h | 1 - src/main.c | 3 +++ src/rendering.c | 39 ++++++++++++++++++++------------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/context.h b/src/context.h index b046c97..b80da22 100644 --- a/src/context.h +++ b/src/context.h @@ -62,6 +62,8 @@ typedef struct context { bool debug; bool is_running; bool resync_flag; + + bool window_size_has_changed; } t_ctx; extern t_ctx ctx; diff --git a/src/input.h b/src/input.h index f514342..0b7e501 100644 --- a/src/input.h +++ b/src/input.h @@ -54,7 +54,6 @@ struct action_hash_item { struct input_state { struct action_hash_item *action_hash; - SDL_Renderer *renderer; /* some input relates to the screen in some way */ const uint8_t *keyboard_state; /* array of booleans indexed by scancode */ uint32_t mouse_state; /* SDL mouse button bitmask */ t_vec2 mouse_window_position; diff --git a/src/main.c b/src/main.c index 910c040..3e83ffa 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,8 @@ static void poll_events(void) { SDL_Event e; + ctx.window_size_has_changed = false; + while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_QUIT: @@ -37,6 +39,7 @@ static void poll_events(void) { case SDL_WINDOWEVENT_RESIZED: ctx.window_w = e.window.data1; ctx.window_h = e.window.data2; + ctx.window_size_has_changed = true; break; } diff --git a/src/rendering.c b/src/rendering.c index a5d34cd..f9538bf 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -141,26 +141,27 @@ static void render_space(void) { void render(void) { textures_update_atlas(&ctx.texture_cache); - /* TODO: only do this when needed */ /* fit rendering context onto the resizable screen */ - if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { - float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT; - int w = (int)((float)RENDER_BASE_WIDTH * ratio); - glViewport( - ctx.window_w / 2 - w / 2, - 0, - w, - ctx.window_h - ); - } else { - float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH; - int h = (int)((float)RENDER_BASE_HEIGHT * ratio); - glViewport( - 0, - ctx.window_h / 2 - h / 2, - ctx.window_w, - h - ); + if (ctx.window_size_has_changed) { + if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { + float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT; + int w = (int)((float)RENDER_BASE_WIDTH * ratio); + glViewport( + ctx.window_w / 2 - w / 2, + 0, + w, + ctx.window_h + ); + } else { + float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH; + int h = (int)((float)RENDER_BASE_HEIGHT * ratio); + glViewport( + 0, + ctx.window_h / 2 - h / 2, + ctx.window_w, + h + ); + } } glClearColor((1.0f / 255) * 230, -- 2.30.2 From 910e45a44b66ad2c22aa8931092333008bdf8acb Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 17:55:57 +0300 Subject: [PATCH 20/27] rendering.c: fix sprite uv in batches, fix alpha blending of texture atlas --- src/game/scenes/ingame.c | 15 +++++++++++++++ src/rendering.c | 11 ++++------- src/rendering/sprites.h | 5 +++-- src/textures.c | 3 ++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index c4de337..b018f25 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,6 +11,21 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); + push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){255, 0, 0, 255}, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 48, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){0, 255, 0, 255}, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 64, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){0, 0, 255, 255}, + .blend = true }); + push_sprite_ex((t_frect){ .x = 32, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/player/baron-walk.png", .color = (t_color){255, 255, 255, 255}, diff --git a/src/rendering.c b/src/rendering.c index f9538bf..27bd18d 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -71,7 +71,8 @@ static void render_2d(void) { glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glDisable(GL_CULL_FACE); - glDepthFunc(GL_LESS); + glDisable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); const size_t render_queue_len = arrlenu(ctx.render_queue_2d); @@ -85,21 +86,17 @@ static void render_2d(void) { const struct sprite_batch batch = collect_sprite_batch(current, render_queue_len - i); + glDepthRange((double)batch_count / UINT16_MAX, 1.0); + if (batch.blend) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); - glDepthRange((double)batch_count / UINT16_MAX, 1.0); - glDisable(GL_ALPHA_TEST); render_sprites(current, batch.size, false); } else { glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); - glDepthRange((double)batch_count / UINT16_MAX, 1.0); - glDisable(GL_ALPHA_TEST); render_sprites(current, batch.size, true); } diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 6c78b5b..0a7fb79 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -115,8 +115,6 @@ static void render_sprites(const struct primitive_2d primitives[], NULL, GL_STREAM_DRAW); - const t_rect srcrect = - textures_get_srcrect(&ctx.texture_cache, primitives->sprite.texture_key); const t_rect dims = textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key); @@ -130,6 +128,9 @@ static void render_sprites(const struct primitive_2d primitives[], const size_t cur = reversed ? len - i - 1: i; const struct sprite_primitive sprite = primitives[cur].sprite; + const t_rect srcrect = + textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key); + const float wr = (float)srcrect.w / (float)dims.w; const float hr = (float)srcrect.h / (float)dims.h; const float xr = (float)srcrect.x / (float)dims.w; diff --git a/src/textures.c b/src/textures.c index d912581..ca9ea9c 100644 --- a/src/textures.c +++ b/src/textures.c @@ -75,6 +75,7 @@ static void add_new_atlas(struct texture_cache *cache) { a_mask); SDL_FreeFormat(native_format); + SDL_SetSurfaceBlendMode(new_atlas, SDL_BLENDMODE_NONE); SDL_SetSurfaceRLE(new_atlas, true); arrput(cache->atlas_surfaces, new_atlas); arrput(cache->atlas_textures, new_gl_texture()); @@ -108,7 +109,7 @@ static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) { glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, + GL_RGBA8, surface->w, surface->h, 0, -- 2.30.2 From 36dcf14db7ae982b9506cabec13d5920d4a642be Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 27 Jul 2024 18:01:45 +0300 Subject: [PATCH 21/27] resize viewport of window maximization/minimization --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index 3e83ffa..1959a7b 100644 --- a/src/main.c +++ b/src/main.c @@ -37,6 +37,7 @@ static void poll_events(void) { switch (e.window.event) { case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: ctx.window_w = e.window.data1; ctx.window_h = e.window.data2; ctx.window_size_has_changed = true; -- 2.30.2 From 5ddf0eb879467d7b6cd0aade9046db6e7961e1b0 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 01:44:39 +0300 Subject: [PATCH 22/27] elf.c: section limit inference for linux, fixes for stb_ds.h hashing, more compilation flags --- CMakeLists.txt | 25 ++++++++++++--- src/audio.c | 2 +- src/game/scenes/ingame.c | 1 + src/game/scenes/title.c | 1 + src/private/rendering.h | 1 - src/system/linux/elf.c | 68 ++++++++++++++++++++++++++++++++++++++++ src/system/linux/elf.h | 10 ++++++ src/textures.c | 56 +++++++++++++++++++-------------- third-party/stb/stb_ds.h | 6 ++-- 9 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 src/system/linux/elf.c create mode 100644 src/system/linux/elf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e9a8a..d834843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,14 @@ add_subdirectory(third-party/physfs) add_subdirectory(third-party/libxm) +if(UNIX) + set(SYSTEM_SOURCE_FILES + src/system/linux/elf.c + ) +else() + set(SYSTEM_SOURCE_FILES) +endif() + set(SOURCE_FILES third-party/physfs/extras/physfsrwops.c third-party/stb/stb_vorbis.c @@ -49,6 +57,8 @@ set(SOURCE_FILES src/game/scenes/scene.c src/game/scenes/scene.h src/game/scenes/title.c src/game/scenes/title.h src/game/scenes/ingame.c src/game/scenes/ingame.h + + ${SYSTEM_SOURCE_FILES} ) # target @@ -90,19 +100,24 @@ else() # these SHOULDN'T break anything... -fno-math-errno -ffp-contract=fast - -fno-signed-zeros - -fno-trapping-math - -freciprocal-math) + -fno-signed-zeros) set(BUILD_FLAGS_RELEASE + -O3 -flto -Wl,-gc-sections -fdata-sections - -ffunction-sections) + -ffunction-sections + -funroll-loops + -fomit-frame-pointer + -fallow-store-data-races) set(BUILD_FLAGS_DEBUG + -O0 -g3 -gdwarf + -fno-omit-frame-pointer + -fstack-protector-all -fsanitize=undefined - -fsanitize-trap=undefined) + -fsanitize=address) target_compile_options(${PROJECT_NAME} PRIVATE ${WARNING_FLAGS} ${BUILD_FLAGS} diff --git a/src/audio.c b/src/audio.c index 7a28241..e220178 100644 --- a/src/audio.c +++ b/src/audio.c @@ -244,7 +244,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel, static uint8_t buffer[16384]; const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t); const int float_buffer_frames = sizeof (buffer) / sizeof (float); - const int stream_frames = len / sizeof (int16_t); + const int stream_frames = len / (int)(sizeof (int16_t)); switch (channel->file_type) { case audio_file_type_ogg: { diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index b018f25..1296ab7 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -65,6 +65,7 @@ static void ingame_end(struct state *state) { struct scene_ingame *scn = (struct scene_ingame *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/game/scenes/title.c b/src/game/scenes/title.c index ed9b32a..7db88df 100644 --- a/src/game/scenes/title.c +++ b/src/game/scenes/title.c @@ -24,6 +24,7 @@ static void title_end(struct state *state) { struct scene_title *scn = (struct scene_title *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/private/rendering.h b/src/private/rendering.h index 8257a6e..d4073e0 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -95,7 +95,6 @@ union uncolored_space_triangle { /* batch of primitives with overlapping properties */ struct mesh_batch { GLuint buffer; /* server side storage */ - size_t buffer_len; /* element count */ uint8_t *primitives; }; diff --git a/src/system/linux/elf.c b/src/system/linux/elf.c new file mode 100644 index 0000000..7377617 --- /dev/null +++ b/src/system/linux/elf.c @@ -0,0 +1,68 @@ +#include "elf.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +bool infer_elf_section_bounds(const char *const restrict name, + const char **restrict vm_start, + const char **restrict vm_end) +{ + bool result = false; + char buf[PATH_MAX]; + ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX); + if (l == -1) + goto ERR_CANT_READLINK; + buf[l] = 0; /* readlink() doesn't write a terminator */ + + int elf = open(buf, O_RDONLY); + if (elf == -1) + goto ERR_CANT_OPEN_SELF; + + /* elf header */ + Elf64_Ehdr ehdr; + read(elf, &ehdr, sizeof ehdr); + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) + goto ERR_NOT_ELF; + + /* section header string table */ + Elf64_Shdr shstrdr; + lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET); + read(elf, &shstrdr, sizeof shstrdr); + char *sh = malloc(shstrdr.sh_size); + lseek(elf, shstrdr.sh_offset, SEEK_SET); + read(elf, sh, shstrdr.sh_size); + + /* walk sections searching for needed name */ + lseek(elf, ehdr.e_shoff, SEEK_SET); + for (size_t s = 0; s < ehdr.e_shnum; ++s) { + Elf64_Shdr shdr; + read(elf, &shdr, sizeof shdr); + + if (strcmp(&sh[shdr.sh_name], name) == 0) { + result = true; + *vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr; + *vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size; + break; + } + } + + free(sh); + +ERR_NOT_ELF: + close(elf); + +ERR_CANT_OPEN_SELF: +ERR_CANT_READLINK: + return result; +} diff --git a/src/system/linux/elf.h b/src/system/linux/elf.h new file mode 100644 index 0000000..23790d7 --- /dev/null +++ b/src/system/linux/elf.h @@ -0,0 +1,10 @@ +#ifndef ELF_H +#define ELF_H + +#include + +bool infer_elf_section_bounds(const char *restrict name, + const char **restrict vm_start, + const char **restrict vm_end); + +#endif diff --git a/src/textures.c b/src/textures.c index ca9ea9c..5ba34cf 100644 --- a/src/textures.c +++ b/src/textures.c @@ -295,8 +295,9 @@ void textures_dump_atlases(struct texture_cache *cache) { static t_texture_key textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ - if (shgeti(cache->hash, path) >= 0) - return (t_texture_key){0}; + const ptrdiff_t i = shgeti(cache->hash, path); + if (i >= 0) + return (t_texture_key){ (uint16_t)i }; SDL_Surface *surface = image_to_surface(path); struct texture new_texture = {0}; @@ -308,12 +309,12 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path upload_texture_from_surface(new_texture.loner_texture, surface); new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; shput(cache->hash, path, new_texture); - return (t_texture_key){ shgeti(cache->hash, path) }; + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; - return (t_texture_key){ shgeti(cache->hash, path) }; + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } } @@ -363,35 +364,44 @@ void textures_update_atlas(struct texture_cache *cache) { } /* EXPERIMANTAL: LIKELY TO BE REMOVED! */ -/* todo: If it's proven to be useful: add runtime checking for .rodata > .data */ -#ifdef __unix__ /* use rodata elf section for fast lookups of repeating textures */ +#ifdef __linux__ /* use rodata elf section for fast lookups of repeating textures */ -extern const char start_rodata_address[]; -extern const char stop_rodata_heuristic[]; +#include "system/linux/elf.h" -asm(".set start_rodata_address, .rodata"); -asm(".set stop_rodata_heuristic, .data"); /* there's nothing in default linker script to know the size of .rodata */ +static const char *rodata_start; +static const char *rodata_stop; -/* TODO: it might be better to contruct a new table that hashes pointers, not strings, to texture keys */ -/* this way every used texture will benefit, no matter the order of commission */ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { static const char *last_path = NULL; static t_texture_key last_texture; + static struct ptr_to_texture { + const void *key; + t_texture_key value; + } *ptr_to_texture; - /* fast path */ + if (rodata_stop == NULL) + if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop)) + CRY("Section inference", ".rodata section lookup failed"); + + /* the fastest path */ if (path == last_path) return last_texture; + else { + /* moderately fast path, by pointer hashing */ + const ptrdiff_t texture = hmgeti(ptr_to_texture, path); + if (texture != -1) { + if (path >= rodata_start && path < rodata_stop) + last_path = path; + last_texture = ptr_to_texture[texture].value; + return last_texture; + } + } - /* hash tables are assumed to be stable, so we just return indices */ - ptrdiff_t texture = shgeti(cache->hash, path); + /* try loading */ + last_texture = textures_load(cache, path); + hmput(ptr_to_texture, path, last_texture); - /* load it if it isn't */ - if (texture == -1) { - last_texture = textures_load(cache, path); - } else - last_texture = (t_texture_key){ (uint16_t)texture }; - - if (path >= start_rodata_address && path < stop_rodata_heuristic) + if (path >= rodata_start && path < rodata_stop) last_path = path; return last_texture; @@ -400,7 +410,7 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { #else t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { /* hash tables are assumed to be stable, so we just return indices */ - ptrdiff_t texture = shgeti(cache->hash, path); + const ptrdiff_t texture = shgeti(cache->hash, path); /* load it if it isn't */ if (texture == -1) { diff --git a/third-party/stb/stb_ds.h b/third-party/stb/stb_ds.h index e84c82d..5330d21 100644 --- a/third-party/stb/stb_ds.h +++ b/third-party/stb/stb_ds.h @@ -1121,7 +1121,7 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) unsigned char *d = (unsigned char *) p; if (len == 4) { - unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + uint32_t hash = (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24); #if 0 // HASH32-A Bob Jenkin's hash function w/o large constants hash ^= seed; @@ -1177,8 +1177,8 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) return (((size_t) hash << 16 << 16) | hash) ^ seed; } else if (len == 8 && sizeof(size_t) == 8) { - size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + size_t hash = (size_t)d[0] | ((size_t)d[1] << 8) | ((size_t)d[2] << 16) | ((size_t)d[3] << 24); + hash |= ((size_t)d[4] | ((size_t)d[5] << 8) | ((size_t)d[6] << 16) | ((size_t)d[7] << 24)) << 16 << 16; hash ^= seed; hash = (~hash) + (hash << 21); hash ^= STBDS_ROTATE_RIGHT(hash,24); -- 2.30.2 From bbcc49d9252fbea69a3c29806bad3f93ed582576 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 01:44:52 +0300 Subject: [PATCH 23/27] let there be light.png --- data/assets/light.png | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/assets/light.png diff --git a/data/assets/light.png b/data/assets/light.png new file mode 100644 index 0000000..128161f --- /dev/null +++ b/data/assets/light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4f651e3cf4106856a20519334f3b239305ec014046312f698a70a1df1ce319b +size 1309 -- 2.30.2 From c59708d619fe411d52d2aaf7a2c114947fe94a01 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 13:33:29 +0300 Subject: [PATCH 24/27] return CMakeLists.txt flags --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d834843..cbd9348 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,9 @@ else() # these SHOULDN'T break anything... -fno-math-errno -ffp-contract=fast - -fno-signed-zeros) + -fno-signed-zeros + -fno-trapping-math + -freciprocal-math) set(BUILD_FLAGS_RELEASE -O3 -flto -- 2.30.2 From ea4d12212cc2d128119b725b67a51664c06663c7 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 14:39:23 +0300 Subject: [PATCH 25/27] rendering.c: fast cos from sin calculation, with lossy fast_sqrt() --- src/rendering/sprites.h | 178 ++++++++++++++++++++-------------------- src/system/linux/elf.c | 82 +++++++++--------- src/util.h | 23 ++++++ 3 files changed, 154 insertions(+), 129 deletions(-) diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 0a7fb79..45cd429 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -5,6 +5,7 @@ #include "../textures.h" #include "../rendering.h" #include "../context.h" +#include "../util.h" #include "quad_element_buffer.h" #include @@ -19,7 +20,7 @@ * before anything is really rendered */ /* TODO: it might make sense to infer alpha channel presence / meaningfulness for textures in atlas */ -/* so that they are rendered with no blend / batched in a way to reduce overdraw automatically */ +/* so that they are rendered with no blend / batched in a way to reduce overdraw automatically */ void push_sprite(char *path, t_frect rect) { struct sprite_primitive sprite = { .rect = rect, @@ -104,7 +105,7 @@ static void render_sprites(const struct primitive_2d primitives[], const size_t len, const bool reversed) { - /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ + /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ static GLuint vertex_array = 0; if (vertex_array == 0) glGenBuffers(1, &vertex_array); @@ -128,103 +129,104 @@ static void render_sprites(const struct primitive_2d primitives[], const size_t cur = reversed ? len - i - 1: i; const struct sprite_primitive sprite = primitives[cur].sprite; - const t_rect srcrect = - textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key); + const t_rect srcrect = + textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key); - const float wr = (float)srcrect.w / (float)dims.w; - const float hr = (float)srcrect.h / (float)dims.h; - const float xr = (float)srcrect.x / (float)dims.w; - const float yr = (float)srcrect.y / (float)dims.h; + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; - /* non-rotated case */ - if (sprite.rotation == 0.0f) { - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - sprite.rect.x, - sprite.rect.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* non-rotated case */ + if (sprite.rotation == 0.0f) { + payload[i] = (struct sprite_primitive_payload) { + /* upper-left */ + .v0 = { + sprite.rect.x, + sprite.rect.y }, + .uv0 = { + xr + wr * sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* bottom-left */ - .v1 = { - (sprite.rect.x), - (sprite.rect.y + sprite.rect.h) }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-left */ + .v1 = { + (sprite.rect.x), + (sprite.rect.y + sprite.rect.h) }, + .uv1 = { + xr + wr * sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* bottom-right */ - .v2 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y + sprite.rect.h) }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-right */ + .v2 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y + sprite.rect.h) }, + .uv2 = { + xr + wr * !sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* upper-right */ - .v3 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y) }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* upper-right */ + .v3 = { + (sprite.rect.x + sprite.rect.w), + (sprite.rect.y) }, + .uv3 = { + xr + wr * !sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* equal for all (flat shaded) */ - .c0 = sprite.color, - .c1 = sprite.color, - .c2 = sprite.color, - .c3 = sprite.color, - }; - } else { - /* rotated case */ - const t_fvec2 c = frect_center(sprite.rect); - const t_fvec2 d = { - .x = (cosf(sprite.rotation + (float)M_PI_4) * sprite.rect.w) * (float)M_SQRT1_2, - .y = (sinf(sprite.rotation + (float)M_PI_4) * sprite.rect.h) * (float)M_SQRT1_2, - }; + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + } else { + /* rotated case */ + const t_fvec2 c = frect_center(sprite.rect); + const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); + const t_fvec2 d = { + .x = t.x * sprite.rect.w * (float)M_SQRT1_2, + .y = t.y * sprite.rect.h * (float)M_SQRT1_2, + }; - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - c.x - d.x, - c.y - d.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + payload[i] = (struct sprite_primitive_payload) { + /* upper-left */ + .v0 = { + c.x - d.x, + c.y - d.y }, + .uv0 = { + xr + wr * sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* bottom-left */ - .v1 = { - c.x - d.y, - c.y + d.x }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-left */ + .v1 = { + c.x - d.y, + c.y + d.x }, + .uv1 = { + xr + wr * sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* bottom-right */ - .v2 = { - c.x + d.x, - c.y + d.y }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + /* bottom-right */ + .v2 = { + c.x + d.x, + c.y + d.y }, + .uv2 = { + xr + wr * !sprite.flip_x, + yr + hr * !sprite.flip_y, }, - /* upper-right */ - .v3 = { - c.x + d.y, - c.y - d.x }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* upper-right */ + .v3 = { + c.x + d.y, + c.y - d.x }, + .uv3 = { + xr + wr * !sprite.flip_x, + yr + hr * sprite.flip_y, }, - /* equal for all (flat shaded) */ - .c0 = sprite.color, - .c1 = sprite.color, - .c2 = sprite.color, - .c3 = sprite.color, - }; - } + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + } } glUnmapBuffer(GL_ARRAY_BUFFER); diff --git a/src/system/linux/elf.c b/src/system/linux/elf.c index 7377617..9696c2f 100644 --- a/src/system/linux/elf.c +++ b/src/system/linux/elf.c @@ -12,57 +12,57 @@ #include bool infer_elf_section_bounds(const char *const restrict name, - const char **restrict vm_start, - const char **restrict vm_end) + const char **restrict vm_start, + const char **restrict vm_end) { - bool result = false; - char buf[PATH_MAX]; - ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX); - if (l == -1) - goto ERR_CANT_READLINK; - buf[l] = 0; /* readlink() doesn't write a terminator */ + bool result = false; + char buf[PATH_MAX]; + ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX); + if (l == -1) + goto ERR_CANT_READLINK; + buf[l] = 0; /* readlink() doesn't write a terminator */ - int elf = open(buf, O_RDONLY); - if (elf == -1) - goto ERR_CANT_OPEN_SELF; + int elf = open(buf, O_RDONLY); + if (elf == -1) + goto ERR_CANT_OPEN_SELF; - /* elf header */ - Elf64_Ehdr ehdr; - read(elf, &ehdr, sizeof ehdr); - if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || - ehdr.e_ident[EI_MAG1] != ELFMAG1 || - ehdr.e_ident[EI_MAG2] != ELFMAG2 || - ehdr.e_ident[EI_MAG3] != ELFMAG3) - goto ERR_NOT_ELF; + /* elf header */ + Elf64_Ehdr ehdr; + read(elf, &ehdr, sizeof ehdr); + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) + goto ERR_NOT_ELF; - /* section header string table */ - Elf64_Shdr shstrdr; - lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET); - read(elf, &shstrdr, sizeof shstrdr); - char *sh = malloc(shstrdr.sh_size); - lseek(elf, shstrdr.sh_offset, SEEK_SET); - read(elf, sh, shstrdr.sh_size); + /* section header string table */ + Elf64_Shdr shstrdr; + lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET); + read(elf, &shstrdr, sizeof shstrdr); + char *sh = malloc(shstrdr.sh_size); + lseek(elf, shstrdr.sh_offset, SEEK_SET); + read(elf, sh, shstrdr.sh_size); - /* walk sections searching for needed name */ - lseek(elf, ehdr.e_shoff, SEEK_SET); - for (size_t s = 0; s < ehdr.e_shnum; ++s) { - Elf64_Shdr shdr; - read(elf, &shdr, sizeof shdr); + /* walk sections searching for needed name */ + lseek(elf, ehdr.e_shoff, SEEK_SET); + for (size_t s = 0; s < ehdr.e_shnum; ++s) { + Elf64_Shdr shdr; + read(elf, &shdr, sizeof shdr); - if (strcmp(&sh[shdr.sh_name], name) == 0) { - result = true; - *vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr; - *vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size; - break; - } - } + if (strcmp(&sh[shdr.sh_name], name) == 0) { + result = true; + *vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr; + *vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size; + break; + } + } - free(sh); + free(sh); ERR_NOT_ELF: - close(elf); + close(elf); ERR_CANT_OPEN_SELF: ERR_CANT_READLINK: - return result; + return result; } diff --git a/src/util.h b/src/util.h index 98b2fae..10b02af 100644 --- a/src/util.h +++ b/src/util.h @@ -154,5 +154,28 @@ void tick_ftimer(float *value); /* returns true if value was cycled */ bool repeat_ftimer(float *value, float at); +/* http://www.azillionmonkeys.com/qed/sqroot.html */ +static inline float fast_sqrt(float x) +{ + union { + float f; + uint32_t u; + } pun = {.f = x}; + + pun.u += 127 << 23; + pun.u >>= 1; + + return pun.f; +} + + +static inline t_fvec2 fast_cossine(float a) { + const float s = sinf(a); + return (t_fvec2){ + .x = fast_sqrt(1.0f - s * s) * (a >= (float)M_PI_2 && a < (float)(M_PI + M_PI_2) ? -1 : 1), + .y = s + }; +} + #endif -- 2.30.2 From 20e33fe30dbdc62d2e97d34f3221fbec552ace03 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 16:06:47 +0300 Subject: [PATCH 26/27] rendering.c: sprite batches with no color information when appropriate --- src/private/rendering.h | 22 ---- src/rendering.c | 13 +-- src/rendering/sprites.h | 229 +++++++++++++++++++++++++--------------- 3 files changed, 142 insertions(+), 122 deletions(-) diff --git a/src/private/rendering.h b/src/private/rendering.h index d4073e0..d15467c 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -20,28 +20,6 @@ struct sprite_primitive { bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */ }; -/* interleaved vertex array data */ -/* TODO: use int16_t for uvs */ -/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ -struct sprite_primitive_payload { - /* upper-left */ - t_fvec2 v0; - t_fvec2 uv0; - t_color c0; - /* bottom-left */ - t_fvec2 v1; - t_fvec2 uv1; - t_color c1; - /* bottom-right */ - t_fvec2 v2; - t_fvec2 uv2; - t_color c2; - /* upper-right */ - t_fvec2 v3; - t_fvec2 uv3; - t_color c3; -}; - struct rect_primitive { t_frect rect; t_color color; diff --git a/src/rendering.c b/src/rendering.c index 27bd18d..e3e51ee 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -88,18 +88,7 @@ static void render_2d(void) { glDepthRange((double)batch_count / UINT16_MAX, 1.0); - if (batch.blend) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthFunc(GL_ALWAYS); - - render_sprites(current, batch.size, false); - } else { - glDisable(GL_BLEND); - glDepthFunc(GL_LESS); - - render_sprites(current, batch.size, true); - } + render_sprites(current, batch); i += batch.size - 1; ++batch_count; break; diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 45cd429..1ea3c98 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -13,6 +13,44 @@ #include #include +/* interleaved vertex array data */ +/* TODO: use int16_t for uvs */ +/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ +struct sprite_primitive_payload { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + t_color c0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + t_color c1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + t_color c2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; + t_color c3; +}; + + +struct sprite_primitive_payload_without_color { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; +}; + /* * an implementation note: * try to avoid doing expensive work in the push functions, @@ -63,8 +101,9 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { static struct sprite_batch { int atlas_id; - size_t size; /* how many primitives are in current batch */ - bool blend; /* whether it's blended or not */ + size_t size; /* how many primitives are in current batch */ + bool blend; /* whether it's blended or not */ + bool colored; /* whether color inmformation is needed to be passed */ } collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { /* assumes that first primitive is already a sprite */ struct sprite_batch result = { @@ -93,6 +132,13 @@ static struct sprite_batch { != result.atlas_id) break; + /* if all are effectively without modulation we can skip sending color data */ + if (!result.colored && (current->sprite.color.r != 255 || + current->sprite.color.g != 255 || + current->sprite.color.b != 255 || + current->sprite.color.a != 255 )) + result.colored = true; + ++result.size; } @@ -102,17 +148,31 @@ static struct sprite_batch { /* assumes that orthogonal matrix setup is done already */ static void render_sprites(const struct primitive_2d primitives[], - const size_t len, - const bool reversed) + const struct sprite_batch batch) { /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ static GLuint vertex_array = 0; if (vertex_array == 0) glGenBuffers(1, &vertex_array); + if (batch.blend) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_ALWAYS); + } else { + glDisable(GL_BLEND); + glDepthFunc(GL_LESS); + } + + size_t payload_size; + if (batch.colored) + payload_size = sizeof (struct sprite_primitive_payload); + else + payload_size = sizeof (struct sprite_primitive_payload_without_color); + glBindBuffer(GL_ARRAY_BUFFER, vertex_array); glBufferData(GL_ARRAY_BUFFER, - sizeof (struct sprite_primitive_payload) * len, + payload_size * batch.size, NULL, GL_STREAM_DRAW); @@ -122,11 +182,10 @@ static void render_sprites(const struct primitive_2d primitives[], /* vertex population over a mapped buffer */ { /* TODO: check errors, ensure alignment ? */ - struct sprite_primitive_payload *const payload = - glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - for (size_t i = 0; i < len; ++i) { - const size_t cur = reversed ? len - i - 1: i; + for (size_t i = 0; i < batch.size; ++i) { + const size_t cur = !batch.blend ? batch.size - i - 1: i; /* render opaques front to back */ const struct sprite_primitive sprite = primitives[cur].sprite; const t_rect srcrect = @@ -137,49 +196,25 @@ static void render_sprites(const struct primitive_2d primitives[], const float xr = (float)srcrect.x / (float)dims.w; const float yr = (float)srcrect.y / (float)dims.h; - /* non-rotated case */ + t_fvec2 uv0 = { xr + wr * sprite.flip_x, yr + hr * sprite.flip_y }; + t_fvec2 uv1 = { xr + wr * sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv2 = { xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv3 = { xr + wr * !sprite.flip_x, yr + hr * sprite.flip_y }; + + t_fvec2 v0, v1, v2, v3; + + /* todo: fast PI/2 degree divisible rotations? */ if (sprite.rotation == 0.0f) { - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - sprite.rect.x, - sprite.rect.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* non-rotated case */ - /* bottom-left */ - .v1 = { - (sprite.rect.x), - (sprite.rect.y + sprite.rect.h) }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + v0 = (t_fvec2){ sprite.rect.x, sprite.rect.y }; + v1 = (t_fvec2){ sprite.rect.x, sprite.rect.y + sprite.rect.h }; + v2 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; + v3 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; - /* bottom-right */ - .v2 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y + sprite.rect.h) }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + } else if (sprite.rect.w == sprite.rect.h) { + /* rotated square case */ - /* upper-right */ - .v3 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y) }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, - - /* equal for all (flat shaded) */ - .c0 = sprite.color, - .c1 = sprite.color, - .c2 = sprite.color, - .c3 = sprite.color, - }; - } else { - /* rotated case */ const t_fvec2 c = frect_center(sprite.rect); const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); const t_fvec2 d = { @@ -187,38 +222,28 @@ static void render_sprites(const struct primitive_2d primitives[], .y = t.y * sprite.rect.h * (float)M_SQRT1_2, }; - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - c.x - d.x, - c.y - d.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + v0 = (t_fvec2){ c.x - d.x, c.y - d.y }; + v1 = (t_fvec2){ c.x - d.y, c.y + d.x }; + v2 = (t_fvec2){ c.x + d.x, c.y + d.y }; + v3 = (t_fvec2){ c.x + d.y, c.y - d.x }; - /* bottom-left */ - .v1 = { - c.x - d.y, - c.y + d.x }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + } else { + /* rotated non-square case*/ - /* bottom-right */ - .v2 = { - c.x + d.x, - c.y + d.y }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + CRY("Rotation", "Unimplemented"); + } - /* upper-right */ - .v3 = { - c.x + d.y, - c.y - d.x }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, + if (batch.colored) + ((struct sprite_primitive_payload *)payload)[i] = (struct sprite_primitive_payload) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, /* equal for all (flat shaded) */ .c0 = sprite.color, @@ -226,37 +251,65 @@ static void render_sprites(const struct primitive_2d primitives[], .c2 = sprite.color, .c3 = sprite.color, }; - } + else + ((struct sprite_primitive_payload_without_color *)payload)[i] = (struct sprite_primitive_payload_without_color) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, + }; } glUnmapBuffer(GL_ARRAY_BUFFER); } + GLsizei off; + GLsizei voff; + GLsizei uvoff; + + if (batch.colored) { + off = offsetof(struct sprite_primitive_payload, v1); + voff = offsetof(struct sprite_primitive_payload, v0); + uvoff = offsetof(struct sprite_primitive_payload, uv0); + } else { + off = offsetof(struct sprite_primitive_payload_without_color, v1); + voff = offsetof(struct sprite_primitive_payload_without_color, v0); + uvoff = offsetof(struct sprite_primitive_payload_without_color, uv0); + } + /* vertex specification */ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, v0)); + off, + (void *)(size_t)voff); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, uv0)); + off, + (void *)(size_t)uvoff); - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, - GL_UNSIGNED_BYTE, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, c0)); + if (batch.colored) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + off, + (void *)offsetof(struct sprite_primitive_payload, c0)); + } else + glColor4ub(255, 255, 255, 255); textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D); bind_quad_element_buffer(); - glDrawElements(GL_TRIANGLES, 6 * (GLsizei)len, GL_UNSIGNED_SHORT, NULL); + glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL); /* clear the state */ glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); -- 2.30.2 From b9188d8d8a9929646268a6a7917483390f3134f4 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 16:25:25 +0300 Subject: [PATCH 27/27] rendering.c: sprite constant color that isn't just white only --- src/game/scenes/ingame.c | 9 ++++---- src/rendering/sprites.h | 44 +++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 1296ab7..666b027 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,10 +11,11 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ - .path = "/assets/light.png", - .color = (t_color){255, 0, 0, 255}, - .blend = true }); + for (size_t i = 100000; --i;) + push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){255, 0, 0, 255}, + .blend = false }); push_sprite_ex((t_frect){ .x = 48, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/light.png", diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 1ea3c98..72ef96d 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -101,17 +101,20 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { static struct sprite_batch { int atlas_id; - size_t size; /* how many primitives are in current batch */ - bool blend; /* whether it's blended or not */ - bool colored; /* whether color inmformation is needed to be passed */ + size_t size; /* how many primitives are in current batch */ + bool blend; /* whether it's blended or not */ + bool constant_colored; /* whether colored batch is uniformly colored */ } collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { /* assumes that first primitive is already a sprite */ - struct sprite_batch result = { + struct sprite_batch batch = { .atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key), .blend = primitives[0].sprite.blend, + .constant_colored = true, }; + const t_color uniform_color = primitives[0].sprite.color; + /* batch size is clamped so that reallocated short indices could be used */ if (len >= QUAD_ELEMENT_BUFFER_LENGTH) len = QUAD_ELEMENT_BUFFER_LENGTH; @@ -124,25 +127,25 @@ static struct sprite_batch { break; /* only collect the same blend modes */ - if (current->sprite.blend != result.blend) + if (current->sprite.blend != batch.blend) break; /* only collect the same texture atlases */ if (textures_get_atlas_id(&ctx.texture_cache, current->sprite.texture_key) - != result.atlas_id) + != batch.atlas_id) break; - /* if all are effectively without modulation we can skip sending color data */ - if (!result.colored && (current->sprite.color.r != 255 || - current->sprite.color.g != 255 || - current->sprite.color.b != 255 || - current->sprite.color.a != 255 )) - result.colored = true; + /* if all are modulated the same we can skip sending the color data */ + if (batch.constant_colored && (current->sprite.color.r != uniform_color.r || + current->sprite.color.g != uniform_color.g || + current->sprite.color.b != uniform_color.b || + current->sprite.color.a != uniform_color.a )) + batch.constant_colored = false; - ++result.size; + ++batch.size; } - return result; + return batch; } @@ -165,7 +168,7 @@ static void render_sprites(const struct primitive_2d primitives[], } size_t payload_size; - if (batch.colored) + if (!batch.constant_colored) payload_size = sizeof (struct sprite_primitive_payload); else payload_size = sizeof (struct sprite_primitive_payload_without_color); @@ -233,7 +236,7 @@ static void render_sprites(const struct primitive_2d primitives[], CRY("Rotation", "Unimplemented"); } - if (batch.colored) + if (!batch.constant_colored) ((struct sprite_primitive_payload *)payload)[i] = (struct sprite_primitive_payload) { .v0 = v0, .v1 = v1, @@ -272,7 +275,7 @@ static void render_sprites(const struct primitive_2d primitives[], GLsizei voff; GLsizei uvoff; - if (batch.colored) { + if (!batch.constant_colored) { off = offsetof(struct sprite_primitive_payload, v1); voff = offsetof(struct sprite_primitive_payload, v0); uvoff = offsetof(struct sprite_primitive_payload, uv0); @@ -296,14 +299,17 @@ static void render_sprites(const struct primitive_2d primitives[], off, (void *)(size_t)uvoff); - if (batch.colored) { + if (!batch.constant_colored) { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_UNSIGNED_BYTE, off, (void *)offsetof(struct sprite_primitive_payload, c0)); } else - glColor4ub(255, 255, 255, 255); + glColor4ub(primitives[0].sprite.color.r, + primitives[0].sprite.color.g, + primitives[0].sprite.color.b, + primitives[0].sprite.color.a); textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D); -- 2.30.2