diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 1198787a..5155c0cf 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -40,8 +40,8 @@ static const char* vertexShaderSource = GLSL_VERSION_DIRECTIVE GLSL_VERTEX_PRECISION "layout(location = 0) in vec2 aPos;\n" - "layout(location = 1) in vec2 aTexCoord;\n" - "layout(location = 2) in vec4 aColor;\n" + "layout(location = 1) in vec4 aColor;\n" + "layout(location = 2) in vec2 aTexCoord;\n" "uniform mat4 uProjection;\n" "out vec2 vTexCoord;\n" "out vec4 vColor;\n" @@ -57,12 +57,16 @@ static const char* fragmentShaderSource = "in vec2 vTexCoord;\n" "in vec4 vColor;\n" "uniform sampler2D uTexture;\n" - "uniform float uAlphaTestRef;\n" // negative = disabled + "uniform float uAlphaTestRef;\n" + "uniform bool uAlphaTestEnabled;\n" "uniform vec4 uFogColor;\n" // rgb = fog color, a = enable flag (0 or 1) "out vec4 fragColor;\n" "void main() {\n" " vec4 c = texture(uTexture, vTexCoord) * vColor;\n" - " if (uAlphaTestRef >= c.a) discard;\n" + " if (uAlphaTestEnabled)" + " {" + " if (uAlphaTestRef >= c.a) discard;\n" + " }" " c.rgb = mix(c.rgb, uFogColor.rgb, uFogColor.a);\n" " fragColor = c;\n" "}\n"; @@ -103,11 +107,60 @@ static GLuint linkProgram(GLuint vertShader, GLuint fragShader) { return program; } +static GLuint linkProgramCompat(GLuint vertShader, GLuint fragShader, bool *success2, Shader *shdr) { + GLuint program = glCreateProgram(); + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + + uint32_t AttributeCount = shdr->vertexAttributeCount; + for (uint32_t i = 0; AttributeCount > i; i++) { + glBindAttribLocation(program, i, shdr->vertexAttributes[i]); + } + + glLinkProgram(program); + + GLint success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog); + fprintf(stderr, "GL: %s Failed To Link: %s\n", shdr->name, infoLog); + abort(); + } else { + *success2 = true; + fprintf(stderr, "GL: %s Linked!\n", shdr->name); + } + return program; +} + // ===[ Batch Flush ]=== static void flushBatch(GLRenderer* gl) { if (gl->batchCount == 0) return; + if (gl->base.CurrentShader != -1) { + GLuint Shader = gl->GMLShaders[gl->base.CurrentShader]; + + GLint UniformCount; + glGetProgramiv(Shader, GL_ACTIVE_UNIFORMS, &UniformCount); + + const GLchar* name = "gm_BaseTexture"; + GLuint index; + + glGetUniformIndices(Shader, 1, &name, &index); + + if (index != GL_INVALID_INDEX) + { + int32_t slot = gl->Sampler2DLookUpTable[gl->base.CurrentShader][index]; + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, gl->currentTextureId); + } + + } else { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, gl->currentTextureId); + } + int32_t singleVertexCount = 0; if (gl->batchType == BATCHTYPE_QUAD) { singleVertexCount = VERTICES_PER_QUAD; @@ -127,7 +180,7 @@ static void flushBatch(GLRenderer* gl) { glBindBuffer(GL_ARRAY_BUFFER, gl->vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * FLOATS_PER_VERTEX * sizeof(float), gl->vertexData); - glBindTexture(GL_TEXTURE_2D, gl->currentTextureId); + if (gl->batchType == BATCHTYPE_QUAD) { glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr); @@ -158,16 +211,67 @@ static void glInit(Renderer* renderer, DataWin* dataWin) { GLRenderer* gl = (GLRenderer*) renderer; renderer->dataWin = dataWin; - // Compile shaders + //compile shaders GLuint vertShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource); GLuint fragShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); gl->shaderProgram = linkProgram(vertShader, fragShader); glDeleteShader(vertShader); glDeleteShader(fragShader); + //yeah find the way to get the shaders here!!! + gl->GMLShaderCompiled = safeMalloc(dataWin->shdr.count * sizeof(bool)); + fprintf(stderr, "GL: %u Shaders Found\n", dataWin->shdr.count); + for (uint32_t i = 0; dataWin->shdr.count > i; i++) { + Shader* shdr = &dataWin->shdr.shaders[i]; + fprintf(stderr, "GL: Compiling %s Vertex Shader\n", shdr->name); + GLuint vertShaderT = compileShader(GL_VERTEX_SHADER, shdr->glsl_Vertex); + fprintf(stderr, "GL: Compiling %s Fragment Shader\n", shdr->name); + GLuint fragShaderT = compileShader(GL_FRAGMENT_SHADER, shdr->glsl_Fragment); + gl->GMLShaderCount++; + gl->GMLShaders = safeRealloc(gl->GMLShaders, gl->GMLShaderCount * sizeof(GLuint)); + gl->Sampler2DLookUpTable = safeRealloc(gl->Sampler2DLookUpTable, gl->GMLShaderCount * sizeof(int32_t*)); + bool success; + gl->GMLShaders[i] = linkProgramCompat(vertShaderT, fragShaderT, &success, shdr); + gl->GMLShaderCompiled[i] = success; + glDeleteShader(vertShaderT); + glDeleteShader(fragShaderT); + //Texture Set Stage BS has to be done bruh :( + int32_t SamplerIndex = 0; + GLint UniformCount; + glGetProgramiv(gl->GMLShaders[i], GL_ACTIVE_UNIFORMS, &UniformCount); + + //I know it looks baddd.... butttt it works + gl->Sampler2DLookUpTable[i] = safeMalloc(UniformCount * sizeof(int32_t)); + GLint LongestUniformName = 0; + glGetProgramiv(gl->GMLShaders[i], GL_ACTIVE_UNIFORM_MAX_LENGTH, &LongestUniformName); + char *UniformName = safeMalloc(LongestUniformName+1); + + for (GLint b = 0; b < UniformCount; b++) { + + GLsizei length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(gl->GMLShaders[i], b, LongestUniformName, &length, &size, &type, UniformName); + + gl->Sampler2DLookUpTable[i][b] = -1; + if (type == GL_SAMPLER_2D) + { + GLint location = glGetUniformLocation(gl->GMLShaders[i], UniformName); + glUseProgram(gl->GMLShaders[i]); + glUniform1i(location, SamplerIndex); + gl->Sampler2DLookUpTable[i][b] = SamplerIndex; + SamplerIndex += 1; + } + + } + + free(UniformName); + + } gl->uProjection = glGetUniformLocation(gl->shaderProgram, "uProjection"); gl->uTexture = glGetUniformLocation(gl->shaderProgram, "uTexture"); gl->uAlphaTestRef = glGetUniformLocation(gl->shaderProgram, "uAlphaTestRef"); + gl->uAlphaTestEnabled = glGetUniformLocation(gl->shaderProgram, "uAlphaTestEnabled"); gl->uFogColor = glGetUniformLocation(gl->shaderProgram, "uFogColor"); gl->alphaTestEnable = false; gl->alphaTestRef = 0.0f; @@ -213,9 +317,9 @@ static void glInit(Renderer* renderer, DataWin* dataWin) { int32_t stride = FLOATS_PER_VERTEX * (int32_t) sizeof(float); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*) 0); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*) (2 * sizeof(float))); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, (void*) (4 * sizeof(float))); glEnableVertexAttribArray(1); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, (void*) (4 * sizeof(float))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, (void*) (2 * sizeof(float))); glEnableVertexAttribArray(2); glBindVertexArray(0); @@ -261,6 +365,92 @@ static void glInit(Renderer* renderer, DataWin* dataWin) { fprintf(stderr, "GL: Renderer initialized (%u texture pages)\n", gl->textureCount); } +static void glGpuResetShader(Renderer* renderer) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + glUseProgram(gl->shaderProgram); + renderer->CurrentShader = -1; +} + +static void glGpuSetShader(Renderer* renderer, int32_t ShaderIndex) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + GLuint Shader = gl->GMLShaders[ShaderIndex]; + glUseProgram(Shader); + //Gotta set those built-ins! they ain't gonna set themselves + GLint gm_Matrices0 = glGetUniformLocation(Shader, "gm_Matrices[0]"); + GLint gm_Matrices1 = glGetUniformLocation(Shader, "gm_Matrices[1]"); + GLint gm_Matrices2 = glGetUniformLocation(Shader, "gm_Matrices[2]"); + GLint gm_Matrices3 = glGetUniformLocation(Shader, "gm_Matrices[3]"); + GLint gm_Matrices4 = glGetUniformLocation(Shader, "gm_Matrices[4]"); + + + GLint gm_FogStart = glGetUniformLocation(Shader, "gm_FogStart"); + GLint gm_RcpFogRange = glGetUniformLocation(Shader, "gm_RcpFogRange"); + GLint gm_PS_FogEnabled = glGetUniformLocation(Shader, "gm_PS_FogEnabled"); + GLint gm_FogColour = glGetUniformLocation(Shader, "gm_FogColour"); + GLint gm_VS_FogEnabled = glGetUniformLocation(Shader, "gm_VS_FogEnabled"); + + //Lights are for another time + + GLint gm_AlphaTestEnabled = glGetUniformLocation(Shader, "gm_AlphaTestEnabled"); + GLint gm_AlphaRefValue = glGetUniformLocation(Shader, "gm_AlphaRefValue"); + + + + if (gm_Matrices0 != -1) { + glUniformMatrix4fv(gm_Matrices0, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_VIEW].m); + } + if (gm_Matrices1 != -1) { + glUniformMatrix4fv(gm_Matrices1, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_PROJECTION].m); + } + if (gm_Matrices2 != -1) { + glUniformMatrix4fv(gm_Matrices2, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_WORLD].m); + } + if (gm_Matrices3 != -1) { + glUniformMatrix4fv(gm_Matrices3, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_WORLD_VIEW].m); + } + if (gm_Matrices4 != -1) { + glUniformMatrix4fv(gm_Matrices4, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION].m); + } + + + if (gm_FogColour != -1) { + glUniform1i(gm_FogColour, gl->fogColor); + } + + if (gm_AlphaTestEnabled != -1) { + glUniform1i(gm_AlphaTestEnabled, gl->alphaTestEnable); + } + if (gm_AlphaRefValue != -1) { + glUniform1f(gm_AlphaRefValue, gl->alphaTestRef); + } + + renderer->CurrentShader = ShaderIndex; +} + +static void glShaderSettingsRefresh(Renderer* renderer) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + if (renderer->CurrentShader != -1) { + glGpuSetShader(renderer, (int32_t) renderer->CurrentShader); + } else { + + float FogR = (float) BGR_R(gl->fogColor) / 255.0f; + float FoGG = (float) BGR_G(gl->fogColor) / 255.0f; + float FogB = (float) BGR_B(gl->fogColor) / 255.0f; + + glUseProgram(gl->shaderProgram); + glUniformMatrix4fv(gl->uProjection, 1, GL_FALSE, renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION].m); + glUniform4f(gl->uFogColor, FogR, FoGG, FogB, gl->fogEnable ? 1.0f : 0.0f); + glUniform1f(gl->uAlphaTestRef, gl->alphaTestRef); + glUniform1i(gl->uAlphaTestEnabled, gl->alphaTestEnable); + glUniform1i(gl->uTexture, 1); + } +} + + + static void glDestroy(Renderer* renderer) { GLRenderer* gl = (GLRenderer*) renderer; @@ -333,10 +523,9 @@ static void glBeginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_ Matrix4f projection; Matrix4f_viewProjection(&projection, (float) viewX, (float) viewY, (float) viewW, (float) viewH, viewAngle); - glUseProgram(gl->shaderProgram); - glUniformMatrix4fv(gl->uProjection, 1, GL_FALSE, projection.m); - glUniform1i(gl->uTexture, 0); - glActiveTexture(GL_TEXTURE0); + renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; + glShaderSettingsRefresh(renderer); + glActiveTexture(GL_TEXTURE1); glBindVertexArray(gl->vao); renderer->PreviousViewMatrix = projection; @@ -384,10 +573,9 @@ static void glBeginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t p Matrix4f projection; Matrix4f_guiProjection(&projection, (float) guiW, (float) guiH, (float) portW, (float) portH); - glUseProgram(gl->shaderProgram); - glUniformMatrix4fv(gl->uProjection, 1, GL_FALSE, projection.m); - glUniform1i(gl->uTexture, 0); - glActiveTexture(GL_TEXTURE0); + renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; + glShaderSettingsRefresh(renderer); + glActiveTexture(GL_TEXTURE1); glBindVertexArray(gl->vao); } @@ -465,8 +653,8 @@ bool GLRenderer_ensureTextureLoaded(GLRenderer* gl, uint32_t pageId) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); free(pixels); fprintf(stderr, "GL: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); @@ -1377,23 +1565,26 @@ static bool glSetRenderTarget(Renderer* renderer, int32_t surfaceId) { if (gl->surfaces[surfaceId] == 0) return false; glBindFramebuffer(GL_FRAMEBUFFER, gl->surfaces[surfaceId]); - glUseProgram(gl->shaderProgram); - glUniform1i(gl->uTexture, 0); + if (surfaceId == renderer->runner->applicationSurfaceId) { - glUniformMatrix4fv(gl->uProjection, 1, GL_FALSE, renderer->PreviousViewMatrix.m); glViewport(gl->base.CPortX, gl->base.CPortY, gl->base.CPortW, gl->base.CPortH); glEnable(GL_SCISSOR_TEST); + renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION] = renderer->PreviousViewMatrix; + glShaderSettingsRefresh(renderer); return true; } // Normal surface bind: surface-local ortho covering the whole surface, no scissor. Matrix4f projection; Matrix4f_identity(&projection); - Matrix4f_ortho(&projection, 0.0f, (float) gl->surfaceWidth[surfaceId], (float) gl->surfaceHeight[surfaceId], 0.0f, -1.0f, 1.0f); - glUniformMatrix4fv(gl->uProjection, 1, GL_FALSE, projection.m); + Matrix4f_ortho(&projection, 0.0f, (float) gl->surfaceWidth[surfaceId], 0.0f, (float) gl->surfaceHeight[surfaceId], -1.0f, 1.0f); glViewport(0, 0, gl->surfaceWidth[surfaceId], gl->surfaceHeight[surfaceId]); glDisable(GL_SCISSOR_TEST); + renderer->GML_Matrices[MATRIX_WORLD_VIEW_PROJECTION] = projection; + glShaderSettingsRefresh(renderer); + + return true; } @@ -1434,8 +1625,8 @@ static void glDrawSurface(Renderer* renderer, int32_t surfaceID, int32_t srcLeft // Texture is bottom-up in GL; GML src is top-down, so flip V. float u0 = (float) srcLeft / (float) texW; float u1 = (float) (srcLeft + srcWidth) / (float) texW; - float v0 = 1.0f - (float) srcTop / (float) texH; - float v1 = 1.0f - (float) (srcTop + srcHeight) / (float) texH; + float v1 = (float) srcTop / (float) texH; + float v0 = (float) (srcTop + srcHeight) / (float) texH; float localX1 = (float) srcWidth; float localY1 = (float) srcHeight; @@ -1446,10 +1637,10 @@ static void glDrawSurface(Renderer* renderer, int32_t surfaceID, int32_t srcLeft Matrix4f_setTransform2D(&transform, x, y, xscale, yscale, angleRad); float x0, y0, x1, y1, x2, y2, x3, y3; - Matrix4f_transformPoint(&transform, 0.0f, 0.0f, &x0, &y0); // top-left - Matrix4f_transformPoint(&transform, localX1, 0.0f, &x1, &y1); // top-right - Matrix4f_transformPoint(&transform, localX1, localY1, &x2, &y2); // bottom-right - Matrix4f_transformPoint(&transform, 0.0f, localY1, &x3, &y3); // bottom-left + Matrix4f_transformPoint(&transform, 0.0f, localY1, &x0, &y0); // top-left + Matrix4f_transformPoint(&transform, localX1, localY1, &x1, &y1); // top-right + Matrix4f_transformPoint(&transform, localX1, 0.0, &x2, &y2); // bottom-right + Matrix4f_transformPoint(&transform, 0.0f, 0.0, &x3, &y3); // bottom-left float r = (float) BGR_R(color) / 255.0f; float g = (float) BGR_G(color) / 255.0f; @@ -1484,26 +1675,11 @@ static int32_t glCreateSpriteFromSurface(Renderer* renderer, int32_t surfaceID, flushBatch(gl); glBindFramebuffer(GL_READ_FRAMEBUFFER, gl->surfaces[surfaceID]); - // OpenGL Y is bottom-up, GML Y is top-down; flip Y to the GL coordinate. - int32_t glY = gl->surfaceHeight[surfaceID] - y - h; uint8_t* pixels = safeMalloc((size_t) w * (size_t) h * 4); if (pixels == nullptr) return -1; - - glReadPixels(x, glY, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Flip vertically (OpenGL reads bottom-to-top) - size_t rowBytes = (size_t) w * 4; - uint8_t* rowTemp = safeMalloc(rowBytes); - repeat(h / 2, row) { - uint8_t* top = pixels + row * rowBytes; - uint8_t* bot = pixels + (h - 1 - row) * rowBytes; - memcpy(rowTemp, top, rowBytes); - memcpy(top, bot, rowBytes); - memcpy(bot, rowTemp, rowBytes); - } - free(rowTemp); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Create a new GL texture from the captured pixels GLuint newTexId; @@ -1512,8 +1688,8 @@ static int32_t glCreateSpriteFromSurface(Renderer* renderer, int32_t surfaceID, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); free(pixels); @@ -1621,8 +1797,7 @@ static void glGpuSetAlphaTestEnable(Renderer* renderer, bool enable) { if (gl->alphaTestEnable == enable) return; flushBatch(gl); gl->alphaTestEnable = enable; - glUseProgram(gl->shaderProgram); - glUniform1f(gl->uAlphaTestRef, enable ? gl->alphaTestRef : -1.0f); + glShaderSettingsRefresh(renderer); } static void glGpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) { @@ -1631,10 +1806,7 @@ static void glGpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) { if (gl->alphaTestRef == refF) return; flushBatch(gl); gl->alphaTestRef = refF; - if (gl->alphaTestEnable) { - glUseProgram(gl->shaderProgram); - glUniform1f(gl->uAlphaTestRef, refF); - } + glShaderSettingsRefresh(renderer); } static void glGpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha) { @@ -1661,11 +1833,147 @@ static void glGpuSetFog(Renderer* renderer, bool enable, uint32_t color) { flushBatch(gl); gl->fogEnable = enable; gl->fogColor = color; - float r = (float) BGR_R(color) / 255.0f; - float g = (float) BGR_G(color) / 255.0f; - float b = (float) BGR_B(color) / 255.0f; - glUseProgram(gl->shaderProgram); - glUniform4f(gl->uFogColor, r, g, b, enable ? 1.0f : 0.0f); + glShaderSettingsRefresh(renderer); +} + +static int32_t glShaderGetUniform(Renderer* renderer, int32_t shaderIndex, char* uniform) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + GLuint Shader = gl->GMLShaders[shaderIndex]; + + return glGetUniformLocation(Shader, uniform); +} + +static int32_t glShaderGetSamplerIndex(Renderer* renderer, int32_t shaderIndex, char* uniform) { + GLRenderer* gl = (GLRenderer*) renderer; + GLuint Shader = gl->GMLShaders[shaderIndex]; + + GLint UniformCount; + glGetProgramiv(Shader, GL_ACTIVE_UNIFORMS, &UniformCount); + + const GLchar* name = uniform; + GLuint index; + + glGetUniformIndices(Shader, 1, &name, &index); + + if (index == GL_INVALID_INDEX) + { + return -1; + } + + return gl->Sampler2DLookUpTable[shaderIndex][index]; +} + +static void glShaderSetUniformF(Renderer* renderer, int32_t handle, int32_t count, float value1, float value2, float value3, float value4) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + int32_t RealCount = count; + if (renderer->CurrentShader != -1) { + GLuint Shader = gl->GMLShaders[renderer->CurrentShader]; + + GLint UniformCount; + glGetProgramiv(Shader, GL_ACTIVE_UNIFORMS, &UniformCount); + GLint LongestUniformName = 0; + glGetProgramiv(Shader, GL_ACTIVE_UNIFORM_MAX_LENGTH, &LongestUniformName); + char *UniformName = safeMalloc(LongestUniformName); + + for (GLint b = 0; b < UniformCount; b++) { + GLsizei length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(Shader, b, LongestUniformName, &length, &size, &type, UniformName); + + int32_t location = glGetUniformLocation(Shader, UniformName); + if (location == handle) + { + if (type == GL_FLOAT) RealCount = 1; + if (type == GL_FLOAT_VEC2) RealCount = 2; + if (type == GL_FLOAT_VEC3) RealCount = 3; + if (type == GL_FLOAT_VEC4) RealCount = 4; + } + } + free(UniformName); + } + + if (RealCount == 1) { + glUniform1f(handle, value1); + } else if (RealCount == 2) { + glUniform2f(handle, value1, value2); + } else if (RealCount == 3) { + glUniform3f(handle, value1, value2, value3); + } else if (RealCount == 4) { + glUniform4f(handle, value1, value2, value3, value4); + } + +} + +static uint32_t glSpriteGetTexture(Renderer* renderer, int32_t tpagIndex) { + GLRenderer* gl = (GLRenderer*) renderer; + TexturePageItem* tpag; + GLuint texId; + int32_t texW, texH; + if (!resolveSpriteTexture(gl, tpagIndex, &tpag, &texId, &texW, &texH)) return 0; + + return texId; +} + +static void glTextureSetStage(Renderer* renderer, int32_t slot, uint32_t texID) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + if (slot < 0) { + fprintf(stderr, "GL: Invalid Texture Stage\n"); + return; + } + if (slot == 0) { + gl->currentTextureId = texID; + } + if (slot > MAX_TEXTURE_STAGES) { + fprintf(stderr, "GL: Texture Stage Higher Than Max\n"); + return; + } + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texID); + glActiveTexture(GL_TEXTURE1); + +} + +static float glTextureGetTexelWidth(Renderer* renderer, uint32_t texID) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + GLint width = 0; + GLint prev; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); + glBindTexture(GL_TEXTURE_2D, texID); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glBindTexture(GL_TEXTURE_2D, prev); + + if (width == 0) return 1.0; + return (1.0 / (float) width); +} + +static float glTextureGetTexelHeight(Renderer* renderer, uint32_t texID) { + GLRenderer* gl = (GLRenderer*) renderer; + flushBatch(gl); + GLint height = 0; + GLint prev; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); + glBindTexture(GL_TEXTURE_2D, texID); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + glBindTexture(GL_TEXTURE_2D, prev); + + if (height == 0) return 1.0; + return (1.0 / (float) height); +} + +static bool glShaderIsCompiled(Renderer* renderer, int32_t shaderID) { + GLRenderer* gl = (GLRenderer*) renderer; + DataWin* dw = gl->base.dataWin; + if (0 > shaderID || shaderID >= dw->shdr.count) return false; + return gl->GMLShaderCompiled[shaderID]; +} + +static bool glShadersSupported(Renderer* renderer) { + return true; } // ===[ Vtable ]=== @@ -1722,12 +2030,24 @@ Renderer* GLRenderer_create(void) { glVtable.drawSurface = glDrawSurface; glVtable.surfaceResize = glSurfaceResize; glVtable.surfaceFree = glSurfaceFree; + glVtable.gpuSetShader = glGpuSetShader, + glVtable.gpuResetShader = glGpuResetShader, + glVtable.shaderGetUniform = glShaderGetUniform, + glVtable.shaderSetUniformF = glShaderSetUniformF, + glVtable.spriteGetTexture = glSpriteGetTexture, + glVtable.textureGetTexelWidth = glTextureGetTexelWidth, + glVtable.textureGetTexelHeight = glTextureGetTexelHeight, + glVtable.shaderGetSamplerIndex = glShaderGetSamplerIndex, + glVtable.textureSetStage = glTextureSetStage, + glVtable.shaderIsCompiled = glShaderIsCompiled, + glVtable.shadersSupported = glShadersSupported, + gl->base.drawColor = 0xFFFFFF; // white (BGR) gl->base.drawAlpha = 1.0f; gl->base.drawFont = -1; gl->base.drawHalign = 0; gl->base.drawValign = 0; gl->base.circlePrecision = 24; - + gl->base.CurrentShader = -1; return (Renderer*) gl; } diff --git a/src/gl/gl_renderer.h b/src/gl/gl_renderer.h index 67561c50..5eabc199 100644 --- a/src/gl/gl_renderer.h +++ b/src/gl/gl_renderer.h @@ -24,6 +24,12 @@ typedef struct { GLint uTexture; GLint uAlphaTestRef; GLint uFogColor; + GLint uAlphaTestEnabled; + GLuint* GMLShaders; + bool* GMLShaderCompiled; + uint32_t GMLShaderCount; + int32_t** Sampler2DLookUpTable; + GLint** Sampler2DLocationLookUpTable; bool alphaTestEnable; float alphaTestRef; diff --git a/src/gl_common/gl_common.c b/src/gl_common/gl_common.c index e356b55a..f43e0b3d 100644 --- a/src/gl_common/gl_common.c +++ b/src/gl_common/gl_common.c @@ -34,8 +34,9 @@ void GLCommon_beginLetterboxBlit(GLuint fbo) { void GLCommon_endLetterboxBlit(int32_t fboWidth, int32_t fboHeight, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH) { int32_t sx, sy, ex, ey; + glClearColor(0.0, 0.0, 0.0, 1.0); GLCommon_computeLetterbox(gameW, gameH, windowW, windowH, &sx, &sy, &ex, &ey); - glBlitFramebuffer(0, 0, fboWidth, fboHeight, sx, sy, ex, ey, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, fboWidth, fboHeight, sx, ey, ex, sy, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -86,7 +87,7 @@ void GLCommon_surfaceBlit(GLuint* surfaces, int32_t* surfaceWidth, int32_t* surf if (part) { // GL Y is bottom-up; convert GML's top-down (srcX, srcY) source rect. - int32_t srcY0 = srcFboH - srcY - srcH; + int32_t srcY0 = srcY; glBlitFramebuffer(srcX, srcY0, srcX + srcW, srcY0 + srcH, dstX, dstY, dstX + srcW, dstY + srcH, GL_COLOR_BUFFER_BIT, GL_NEAREST); } else { glBlitFramebuffer(0, 0, srcFboW, srcFboH, dstX, dstY, dstX + srcFboW, dstY + srcFboH, GL_COLOR_BUFFER_BIT, GL_NEAREST); diff --git a/src/gl_legacy/gl_legacy_renderer.c b/src/gl_legacy/gl_legacy_renderer.c index 0968cee8..105afc1d 100644 --- a/src/gl_legacy/gl_legacy_renderer.c +++ b/src/gl_legacy/gl_legacy_renderer.c @@ -280,8 +280,8 @@ bool GLLegacyRenderer_ensureTextureLoaded(GLLegacyRenderer* gl, uint32_t pageId) // Nearest is mandatory for index textures, bilinear would interpolate palette indices into nonsense colors. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); free(pixels); #else @@ -302,8 +302,8 @@ bool GLLegacyRenderer_ensureTextureLoaded(GLLegacyRenderer* gl, uint32_t pageId) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); free(pixels); #endif @@ -1218,37 +1218,22 @@ static int32_t glCreateSpriteFromSurface(Renderer* renderer, int32_t surfaceID, if (gl->surfaces[surfaceID] == 0) return -1; glBindFramebuffer(GL_READ_FRAMEBUFFER, gl->surfaces[surfaceID]); - int32_t srcHeight = gl->surfaceHeight[surfaceID]; uint8_t* pixels = safeMalloc((size_t) w * (size_t) h * 4); if (pixels == nullptr) return -1; - // OpenGL Y is bottom-up, GML Y is top-down, so flip the Y coordinate - int32_t glY = srcHeight - y - h; - glReadPixels(x, glY, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Flip vertically (OpenGL reads bottom-to-top) - size_t rowBytes = (size_t) w * 4; - uint8_t* rowTemp = safeMalloc(rowBytes); - repeat(h / 2, row) { - uint8_t* top = pixels + row * rowBytes; - uint8_t* bot = pixels + (h - 1 - row) * rowBytes; - memcpy(rowTemp, top, rowBytes); - memcpy(top, bot, rowBytes); - memcpy(bot, rowTemp, rowBytes); - } - free(rowTemp); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Create a new GL texture from the captured pixels GLuint newTexId; glGenTextures(1, &newTexId); glBindTexture(GL_TEXTURE_2D, newTexId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); free(pixels); @@ -1390,8 +1375,8 @@ static int32_t glLegacyCreateSurface(Renderer* renderer, int32_t width, int32_t glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBindFramebuffer(GL_FRAMEBUFFER, gl->surfaces[surfaceIndex]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->surfaceTexture[surfaceIndex], 0); @@ -1463,8 +1448,8 @@ static void glLegacySurfaceResize(Renderer* renderer, int32_t surfaceId, int32_t glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBindFramebuffer(GL_FRAMEBUFFER, gl->surfaces[surfaceId]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->surfaceTexture[surfaceId], 0); @@ -1515,7 +1500,7 @@ static bool glLegacySetRenderTarget(Renderer* renderer, int32_t surfaceId) { Matrix4f projection; Matrix4f_identity(&projection); - Matrix4f_ortho(&projection, 0.0f, (float) w, (float) h, 0.0f, -1.0f, 1.0f); + Matrix4f_ortho(&projection, 0.0f, (float) w, 0.0f, (float) h, -1.0f, 1.0f); glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection.m); glMatrixMode(GL_MODELVIEW); @@ -1547,11 +1532,11 @@ static void glLegacyDrawSurface(Renderer* renderer, int32_t surfaceId, int32_t s float u0 = (float) srcLeft / (float) texW; float u1 = (float) (srcLeft + srcWidth) / (float) texW; #ifndef PLATFORM_PS3 - float v0 = 1.0f - (float) srcTop / (float) texH; - float v1 = 1.0f - (float) (srcTop + srcHeight) / (float) texH; + float v0 = (float) srcTop / (float) texH; + float v1 = (float) (srcTop + srcHeight) / (float) texH; #else - float v1 = 1.0f - (float) srcTop / (float) texH; - float v0 = 1.0f - (float) (srcTop + srcHeight) / (float) texH; + float v1 = (float) srcTop / (float) texH; + float v0 = (float) (srcTop + srcHeight) / (float) texH; #endif float r = (float) BGR_R(color) / 255.0f; diff --git a/src/matrix_math.h b/src/matrix_math.h index 9c92bae8..451622f1 100644 --- a/src/matrix_math.h +++ b/src/matrix_math.h @@ -149,7 +149,7 @@ static inline Matrix4f* Matrix4f_setTransform2D(Matrix4f* dest, float x, float y // Y-down coordinate space, optionally rotated by angleDeg counter-clockwise about the view center (matching GML view_angle). static inline Matrix4f* Matrix4f_viewProjection(Matrix4f* dest, float left, float top, float width, float height, float angleDeg) { Matrix4f_identity(dest); - Matrix4f_ortho(dest, left, left + width, top + height, top, -1.0f, 1.0f); + Matrix4f_ortho(dest, left, left + width, top, top + height, -1.0f, 1.0f); if (angleDeg != 0.0f) { // Rotate the world opposite the camera, about the view center, to spin the camera by angleDeg. diff --git a/src/renderer.h b/src/renderer.h index 7d57ee01..cf417fd3 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -30,6 +30,17 @@ #define bm_inv_dest_color 10 #define bm_src_alpha_sat 11 +#define MATRIX_VIEW 0 +#define MATRIX_PROJECTION 1 +#define MATRIX_WORLD 2 +#define MATRIX_WORLD_VIEW 3 +#define MATRIX_WORLD_VIEW_PROJECTION 4 +#define MATRICES_MAX 5 + +#define MAX_VS_LIGHTS 8 + +#define MAX_TEXTURE_STAGES 8 + // Sentinel returned by ensureApplicationSurface on platforms that don't back the application_surface with a real entry in the renderer's surface table. // // Also used as the initial value of Runner.applicationSurfaceId before the first ensure call. @@ -110,6 +121,18 @@ typedef struct { // Optional: tile a source sub-rect (in tpag source-page space) across a dest rect, for nine-slice Repeat/BlankRepeat at angle 0. // srcX/srcY are post tpag->targetX/Y. nullptr = per-tile drawSpritePart fallback (also used for Mirror and non-zero angle). void (*drawTiledPart)(Renderer* renderer, int32_t tpagIndex, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, float dstX, float dstY, float dstW, float dstH, uint32_t color, float alpha); + // Shader Functions + void (*gpuSetShader)(Renderer* renderer, int32_t shaderIndex); + void (*gpuResetShader)(Renderer* renderer); + int32_t (*shaderGetUniform)(Renderer* renderer, int32_t shaderIndex, char* uniform); + int32_t (*shaderGetSamplerIndex)(Renderer* renderer, int32_t shaderIndex, char* uniform); + void (*shaderSetUniformF)(Renderer* renderer, int32_t handle, int32_t count, float value1, float value2, float value3, float value4); + uint32_t (*spriteGetTexture)(Renderer* renderer, int32_t tpagIndex); + float (*textureGetTexelWidth)(Renderer* renderer, uint32_t texID); + float (*textureGetTexelHeight)(Renderer* renderer, uint32_t texID); + void (*textureSetStage)(Renderer* renderer, int32_t slot, uint32_t texID); + bool (*shaderIsCompiled)(Renderer* renderer, int32_t shader); + bool (*shadersSupported)(Renderer* renderer); } RendererVtable; // ===[ Renderer Base Struct ]=== @@ -130,6 +153,8 @@ struct Renderer { int32_t CPortW; int32_t CPortH; Runner* runner; + Matrix4f GML_Matrices[MATRICES_MAX]; + int32_t CurrentShader; }; // ===[ Shared Helpers (platform-agnostic) ]=== diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 54c6f88c..2a3a68d7 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -12944,6 +12944,224 @@ static RValue builtin_parameter_string(VMContext* ctx, RValue* args, int32_t arg return RValue_makeString(ctx->runner->gameArgs[index]); } +static RValue builtin_shader_set(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t ShaderID = (int32_t) RValue_toReal(args[0]); + //fprintf(stderr, "Set Shader ID %u\n", ShaderID); + //gpuSetShader + if (ctx->runner->renderer->vtable->gpuSetShader != nullptr) { + ctx->runner->renderer->vtable->gpuSetShader(ctx->runner->renderer, ShaderID); + } + return RValue_makeUndefined(); +} + +static RValue builtin_shader_reset(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + if (ctx->runner->renderer->vtable->gpuResetShader != nullptr) { + ctx->runner->renderer->vtable->gpuResetShader(ctx->runner->renderer); + } + return RValue_makeUndefined(); +} + +static RValue builtin_shader_current(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + if (ctx->runner->renderer != nullptr) { + return RValue_makeReal(ctx->runner->renderer->CurrentShader); + } + return RValue_makeReal(-1); +} + +static RValue builtin_shader_is_compiled(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t ShaderID = (int32_t) RValue_toReal(args[0]); + if (ctx->runner->renderer->vtable->shaderIsCompiled != nullptr) { + return RValue_makeBool(ctx->runner->renderer->vtable->shaderIsCompiled(ctx->runner->renderer, ShaderID)); + } + + return RValue_makeBool(false); +} + +static RValue builtin_shader_get_name(VMContext* ctx, RValue* args, MAYBE_UNUSED int32_t argCount) { + int32_t shaderIndex = (int32_t) RValue_toReal(args[0]); + Shader* shdr = &ctx->dataWin->shdr.shaders[shaderIndex]; + if (0 > shaderIndex || (uint32_t) shaderIndex >= ctx->dataWin->shdr.count) return RValue_makeString(""); + const char* name = shdr->name; + return RValue_makeString(name != nullptr ? name : ""); +} + +static RValue builtin_shaders_are_supported(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + if (ctx->runner->renderer->vtable->shadersSupported != nullptr) { + return RValue_makeBool(ctx->runner->renderer->vtable->shadersSupported(ctx->runner->renderer)); + } + + return RValue_makeBool(false); +} + +static RValue builtin_shader_get_uniform(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t ShaderID = (int32_t) RValue_toReal(args[0]); + char* uniform = RValue_toString(args[1]); + if (ctx->runner->renderer->vtable->shaderGetUniform != nullptr) { + return RValue_makeInt32(ctx->runner->renderer->vtable->shaderGetUniform(ctx->runner->renderer, ShaderID, uniform)); + } + return RValue_makeInt32(-1); +} + +static RValue builtin_shader_get_sampler_index(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t ShaderID = (int32_t) RValue_toReal(args[0]); + char* uniform = RValue_toString(args[1]); + if (ctx->runner->renderer->vtable->shaderGetSamplerIndex != nullptr) { + return RValue_makeInt32(ctx->runner->renderer->vtable->shaderGetSamplerIndex(ctx->runner->renderer, ShaderID, uniform)); + } + return RValue_makeInt32(-1); +} + +static RValue builtin_texture_set_stage(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t TextureSlot = (int32_t) RValue_toReal(args[0]); + int32_t texID = (int32_t) RValue_toInt32(args[1]); + if (ctx->runner->renderer->vtable->textureSetStage != nullptr) { + ctx->runner->renderer->vtable->textureSetStage(ctx->runner->renderer, TextureSlot, texID); + } + return RValue_makeUndefined(); +} + +static RValue builtin_shader_set_uniformF(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t handle = (int32_t) RValue_toReal(args[0]); + float value1, value2, value3, value4; + //fprintf(stderr, "Set ARG Count %u\n", argCount); + if (ctx->runner->renderer->vtable->shaderSetUniformF != nullptr) { + //return RValue_makeReal(ctx->runner->renderer->vtable->shaderSetUniformF(ctx->runner->renderer, ShaderID, uniform)); + if (argCount == 2) { + value1 = (float) RValue_toReal(args[1]); + ctx->runner->renderer->vtable->shaderSetUniformF(ctx->runner->renderer, handle, 1, value1, 0.0, 0.0, 0.0); + } else if (argCount == 3) { + value1 = (float) RValue_toReal(args[1]); + value2 = (float) RValue_toReal(args[2]); + ctx->runner->renderer->vtable->shaderSetUniformF(ctx->runner->renderer, handle, 2, value1, value2, 0.0, 0.0); + } else if (argCount == 4) { + value1 = (float) RValue_toReal(args[1]); + value2 = (float) RValue_toReal(args[2]); + value3 = (float) RValue_toReal(args[3]); + ctx->runner->renderer->vtable->shaderSetUniformF(ctx->runner->renderer, handle, 3, value1, value2, value3, 0.0); + } else if (argCount == 5) { + value1 = (float) RValue_toReal(args[1]); + value2 = (float) RValue_toReal(args[2]); + value3 = (float) RValue_toReal(args[3]); + value4 = (float) RValue_toReal(args[4]); + //fprintf(stderr, "Value4 %.8f\n", value4); + ctx->runner->renderer->vtable->shaderSetUniformF(ctx->runner->renderer, handle, 4, value1, value2, value3, value4); + } + + } + return RValue_makeUndefined(); +} + +static RValue builtin_sprite_get_uvs(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + int32_t spriteIndex = (int32_t) RValue_toReal(args[0]); + int32_t subimg = RValue_toInt32(args[1]); + if (0 > subimg && ctx->currentInstance != nullptr) { + subimg = (int32_t) ctx->currentInstance->imageIndex; + } + int32_t TpagIndex = Renderer_resolveTPAGIndex(ctx->dataWin, spriteIndex, subimg); + //I think default texture page size is 2048x2048? + float DivW = 0.00048828125; //1.0/2048.0 + float DivH = 0.00048828125; //1.0/2048.0 + + if (ctx->runner->renderer->vtable->textureGetTexelWidth != nullptr) { + DivW = ctx->runner->renderer->vtable->textureGetTexelWidth(ctx->runner->renderer, ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); + DivH = ctx->runner->renderer->vtable->textureGetTexelHeight(ctx->runner->renderer, ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); + } + + float left = (float) ctx->dataWin->tpag.items[TpagIndex].sourceX * DivW; + float top = (float) ctx->dataWin->tpag.items[TpagIndex].sourceY * DivH; + float right = (float) left + (ctx->dataWin->tpag.items[TpagIndex].sourceWidth * DivW); + float bottom = (float) top + (ctx->dataWin->tpag.items[TpagIndex].sourceHeight * DivH); + float trimmedLeft = (float) ctx->dataWin->tpag.items[TpagIndex].targetX; + float trimmedTop = (float) ctx->dataWin->tpag.items[TpagIndex].targetY; + float NormWidthS = (float) ctx->dataWin->tpag.items[TpagIndex].sourceWidth / (float) ctx->dataWin->tpag.items[TpagIndex].boundingWidth; + float NormHeightS = (float) ctx->dataWin->tpag.items[TpagIndex].sourceHeight / (float) ctx->dataWin->tpag.items[TpagIndex].boundingHeight; + + + GMLArray* out = GMLArray_create(8); + *GMLArray_slot(out, 0) = RValue_makeReal(left); + *GMLArray_slot(out, 1) = RValue_makeReal(top); + *GMLArray_slot(out, 2) = RValue_makeReal(right); + *GMLArray_slot(out, 3) = RValue_makeReal(bottom); + *GMLArray_slot(out, 4) = RValue_makeReal(trimmedLeft); + *GMLArray_slot(out, 5) = RValue_makeReal(trimmedTop); + *GMLArray_slot(out, 6) = RValue_makeReal(NormWidthS); + *GMLArray_slot(out, 7) = RValue_makeReal(NormHeightS); + return RValue_makeArray(out); +} + +static RValue builtin_sprite_get_texture(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + int32_t spriteIndex = (int32_t) RValue_toReal(args[0]); + int32_t subimg = RValue_toInt32(args[1]); + if (0 > subimg && ctx->currentInstance != nullptr) { + subimg = (int32_t) ctx->currentInstance->imageIndex; + } + int32_t TpagIndex = Renderer_resolveTPAGIndex(ctx->dataWin, spriteIndex, subimg); + + return RValue_makeInt32(ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); +} + +static RValue builtin_font_get_uvs(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + int32_t fontIndex = (int32_t) RValue_toReal(args[0]); + + //if (0 > fontIndex || ctx->dataWin->font.count <= (uint32_t) fontIndex) return; + + Font* font = &ctx->runner->dataWin->font.fonts[fontIndex]; + + + int32_t TpagIndex = font->tpagIndex; + //I think default texture page size is 2048x2048? + float DivW = 0.00048828125; //1.0/2048.0 + float DivH = 0.00048828125; //1.0/2048.0 + + if (ctx->runner->renderer->vtable->textureGetTexelWidth != nullptr) { + DivW = ctx->runner->renderer->vtable->textureGetTexelWidth(ctx->runner->renderer, ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); + DivH = ctx->runner->renderer->vtable->textureGetTexelHeight(ctx->runner->renderer, ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); + } + + float left = (float) ctx->dataWin->tpag.items[TpagIndex].sourceX * DivW; + float top = (float) ctx->dataWin->tpag.items[TpagIndex].sourceY * DivH; + float right = (float) left + (ctx->dataWin->tpag.items[TpagIndex].sourceWidth * DivW); + float bottom = (float) top + (ctx->dataWin->tpag.items[TpagIndex].sourceHeight * DivH); + + GMLArray* out = GMLArray_create(4); + *GMLArray_slot(out, 0) = RValue_makeReal(left); + *GMLArray_slot(out, 1) = RValue_makeReal(top); + *GMLArray_slot(out, 2) = RValue_makeReal(right); + *GMLArray_slot(out, 3) = RValue_makeReal(bottom); + return RValue_makeArray(out); +} + +static RValue builtin_font_get_texture(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + //see if it works + int32_t fontIndex = (int32_t) RValue_toReal(args[0]); + Font* font = &ctx->runner->dataWin->font.fonts[fontIndex]; + int32_t TpagIndex = font->tpagIndex; + + return RValue_makeInt32(ctx->runner->renderer->vtable->spriteGetTexture(ctx->runner->renderer, TpagIndex)); +} + +static RValue builtin_texture_get_texel_width(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + uint32_t texID = (uint32_t) RValue_toReal(args[0]); + return RValue_makeReal(ctx->runner->renderer->vtable->textureGetTexelWidth(ctx->runner->renderer, texID)); +} + +static RValue builtin_texture_get_texel_height(VMContext* ctx, MAYBE_UNUSED RValue* args, MAYBE_UNUSED int32_t argCount) { + + uint32_t texID = (uint32_t) RValue_toReal(args[0]); + return RValue_makeReal(ctx->runner->renderer->vtable->textureGetTexelHeight(ctx->runner->renderer, texID)); +} + // ===[ REGISTRATION ]=== void VMBuiltins_registerAll(VMContext* ctx) { @@ -13808,4 +14026,19 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "game_change", builtin_game_change); VM_registerBuiltin(ctx, "parameter_count", builtin_parameter_count); VM_registerBuiltin(ctx, "parameter_string", builtin_parameter_string); + VM_registerBuiltin(ctx, "shader_set", builtin_shader_set); + VM_registerBuiltin(ctx, "shader_reset", builtin_shader_reset); + VM_registerBuiltin(ctx, "shader_current", builtin_shader_current); + VM_registerBuiltin(ctx, "shader_is_compiled", builtin_shader_is_compiled); + VM_registerBuiltin(ctx, "shader_get_name", builtin_shader_get_name); + VM_registerBuiltin(ctx, "shaders_are_supported", builtin_shaders_are_supported); + VM_registerBuiltin(ctx, "shader_get_uniform", builtin_shader_get_uniform); + VM_registerBuiltin(ctx, "shader_get_sampler_index", builtin_shader_get_sampler_index); + VM_registerBuiltin(ctx, "shader_set_uniform_f", builtin_shader_set_uniformF); + VM_registerBuiltin(ctx, "sprite_get_uvs", builtin_sprite_get_uvs); + VM_registerBuiltin(ctx, "sprite_get_texture", builtin_sprite_get_texture); + VM_registerBuiltin(ctx, "font_get_uvs", builtin_font_get_uvs); + VM_registerBuiltin(ctx, "texture_get_texel_width", builtin_texture_get_texel_width); + VM_registerBuiltin(ctx, "texture_get_texel_height", builtin_texture_get_texel_height); + VM_registerBuiltin(ctx, "texture_set_stage", builtin_texture_set_stage); }