From b2ec8e7ff07ebc5982b90b1661d72349bbe10a75 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 03:57:03 +0100 Subject: [PATCH 01/12] feat(vertex): Add vertex and primitives functions --- src/gl/gl_renderer.c | 144 +++++++++ src/renderer.h | 57 ++++ src/vm_builtins.c | 735 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 936 insertions(+) diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 48b0b4480..ced1b3c4f 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1590,6 +1590,149 @@ static void glDrawTriangle(Renderer *renderer, float x1, float y1, float x2, flo } } +static int vertex_type_components(int type) +{ + switch (type) { + case VERTEX_TYPE_FLOAT1: + return 1; + case VERTEX_TYPE_FLOAT2: + return 2; + case VERTEX_TYPE_FLOAT3: + return 3; + case VERTEX_TYPE_FLOAT4: + return 4; + case VERTEX_TYPE_UBYTE4: + return 4; + case VERTEX_TYPE_COLOR: + return 4; + } + + return 4; +} + +static void glDrawVertexBuffer(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture) { + if (!buffer || !buffer->format) + return; + + typedef struct { + GLuint vbo; + } GLVertexBuffer; + + GLVertexBuffer *glBuffer = buffer->rendererData; + + if (!glBuffer) { + glBuffer = malloc(sizeof(GLVertexBuffer)); + glGenBuffers(1, &glBuffer->vbo); + buffer->rendererData = glBuffer; + } + + GLenum mode; + + switch (primitive) { + case PRIMITIVE_POINTS: + mode = GL_POINTS; + break; + + case PRIMITIVE_LINES: + mode = GL_LINES; + break; + + case PRIMITIVE_LINE_STRIP: + mode = GL_LINE_STRIP; + break; + + case PRIMITIVE_TRIANGLES: + mode = GL_TRIANGLES; + break; + + case PRIMITIVE_TRIANGLE_STRIP: + mode = GL_TRIANGLE_STRIP; + break; + + case PRIMITIVE_TRIANGLE_FAN: + mode = GL_TRIANGLE_FAN; + break; + + default: + return; + } + + glBindBuffer(GL_ARRAY_BUFFER, glBuffer->vbo); + glBufferData(GL_ARRAY_BUFFER, buffer->size, buffer->data, GL_DYNAMIC_DRAW); + + for (int i = 0; i < buffer->format->numElements; i++) { + VertexElement *e = &buffer->format->elements[i]; + + switch (e->usage) { + case VERTEX_USAGE_POSITION: + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, + vertex_type_components(e->type), + GL_FLOAT, + GL_FALSE, + buffer->format->stride, + (void*)(intptr_t)e->offset + ); + break; + + case VERTEX_USAGE_COLOR: + glEnableVertexAttribArray(1); + glVertexAttribPointer( + 1, + 4, + GL_UNSIGNED_BYTE, + GL_TRUE, + buffer->format->stride, + (void*)(intptr_t)e->offset + ); + break; + + case VERTEX_USAGE_NORMAL: + glEnableVertexAttribArray(2); + glVertexAttribPointer( + 2, + 3, + GL_FLOAT, + GL_FALSE, + buffer->format->stride, + (void*)(intptr_t)e->offset + ); + break; + + case VERTEX_USAGE_TEXCOORD: + glEnableVertexAttribArray(3); + glVertexAttribPointer( + 3, + 2, + GL_FLOAT, + GL_FALSE, + buffer->format->stride, + (void*)(intptr_t)e->offset + ); + break; + } + } + + if (texture != -1) { + glBindTexture(GL_TEXTURE_2D, texture); + } + + uint32_t vertexCount = buffer->size / buffer->format->stride; + printf("Drawing vertex buffer with %u vertices\n", vertexCount); + glDrawArrays( + mode, + 0, + vertexCount + ); + + for (int i = 0; i < buffer->format->numElements; i++) { + glDisableVertexAttribArray(i); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + // ===[ Text Drawing ]=== // Resolved font state shared between glDrawText and glDrawTextColor @@ -2691,6 +2834,7 @@ Renderer* GLRenderer_create(void) { glVtable.drawLine = glDrawLine; glVtable.drawLineColor = glDrawLineColor; glVtable.drawTriangle = glDrawTriangle; + glVtable.drawVertexBuffer = glDrawVertexBuffer; glVtable.drawText = glDrawText; glVtable.drawTextColor = glDrawTextColor; glVtable.flush = glRendererFlush; diff --git a/src/renderer.h b/src/renderer.h index 83759888c..6d0be397a 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -75,6 +75,62 @@ typedef struct { int32_t dstAlpha; } BlendFactors; +// Vertex + +typedef enum { + PRIMITIVE_POINTS = 0, + PRIMITIVE_LINES = 1, + PRIMITIVE_LINE_STRIP = 2, + PRIMITIVE_TRIANGLES = 3, + PRIMITIVE_TRIANGLE_STRIP = 4, + PRIMITIVE_TRIANGLE_FAN = 5, +} PrimitiveType; + +typedef enum { + VERTEX_USAGE_POSITION = 1, + VERTEX_USAGE_COLOR = 2, + VERTEX_USAGE_NORMAL = 3, + VERTEX_USAGE_TEXCOORD = 4, +} VertexUsage; + +typedef enum { + VERTEX_TYPE_FLOAT1, + VERTEX_TYPE_FLOAT2, + VERTEX_TYPE_FLOAT3, + VERTEX_TYPE_FLOAT4, + VERTEX_TYPE_UBYTE4, + VERTEX_TYPE_COLOR, +} VertexType; + +typedef struct { + VertexUsage usage; + VertexType type; + uint32_t offset; + uint32_t size; +} VertexElement; + +typedef struct { + VertexElement elements[16]; + int numElements; + uint32_t stride; +} VertexFormat; + +typedef struct { + uint8_t *data; + size_t size; + size_t capacity; + + VertexFormat *format; + uint32_t vertexSize; + + uint8_t currentVertex[256]; + uint32_t currentOffset; + + bool isFrozen; + bool vertexStarted; + void* rendererData; // Backend-specific data (e.g., OpenGL buffer ID) +} VertexBuffer; + typedef struct { void (*init)(Renderer* renderer, DataWin* dataWin); void (*destroy)(Renderer* renderer); @@ -100,6 +156,7 @@ typedef struct { void (*drawLineColor)(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha); void (*drawText)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation); void (*drawTextColor)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, float lineSeparation); + void (*drawVertexBuffer)(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture); void (*flush)(Renderer* renderer); void (*clearScreen)(Renderer* renderer, uint32_t color, float alpha); int32_t (*createSpriteFromSurface)(Renderer* renderer, int32_t surfaceID, int32_t x, int32_t y, int32_t w, int32_t h, bool removeback, bool smooth, int32_t xorig, int32_t yorig); diff --git a/src/vm_builtins.c b/src/vm_builtins.c index e0ce4918e..df38b0174 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -16358,6 +16358,699 @@ static RValue builtin_sprite_get_info(VMContext* ctx, RValue* args, int32_t argC return RValue_makeStructAndIncRef(ret); } +// Vertex + +static VertexFormat buildingFormat; +static bool building = false; + +static int nextVertexFormatId = 1; +static VertexFormat vertexFormats[256]; + +static int nextVertexBufferId = 1; +static VertexBuffer *vertexBuffers[256]; + +VertexBuffer *getVertexBuffer(int id) { + if (id <= 0 || id >= nextVertexBufferId) { + return nullptr; + } + VertexBuffer *buffer = vertexBuffers[id]; + return buffer; +} + +static VertexElement *findElement(VertexFormat *format, VertexUsage usage) { + for (int i = 0; i < format->numElements; i++) { + if (format->elements[i].usage == usage) { + return &format->elements[i]; + } + } + return nullptr; +} + +// vertex_format_begin(): begins building a new vertex format. +static RValue builtin_vertex_format_begin(VMContext* ctx, RValue* args, int32_t argCount) { + printf("vertex_format_begin()\n"); + memset(&buildingFormat, 0, sizeof(buildingFormat)); + building = true; + + return RValue_makeReal(0); +} + +static void vertexFormatAdd(VertexUsage usage,VertexType type) { + printf("vertexFormatAdd(usage=%d, type=%d)\n", usage, type); + VertexElement *e = &buildingFormat.elements[buildingFormat.numElements++]; + + e->usage = usage; + e->type = type; + e->offset = buildingFormat.stride; + switch (type) { + case VERTEX_TYPE_FLOAT1: + e->size = sizeof(float); + break; + case VERTEX_TYPE_FLOAT2: + e->size = sizeof(float) * 2; + break; + case VERTEX_TYPE_FLOAT3: + e->size = sizeof(float) * 3; + break; + case VERTEX_TYPE_FLOAT4: + e->size = sizeof(float) * 4; + break; + case VERTEX_TYPE_COLOR: + e->size = 4; + break; + default: + e->size = 0; + break; + } + + buildingFormat.stride += e->size; +} + +static RValue builtin_vertex_format_add_color(VMContext* ctx, RValue* args, int32_t argCount) { + vertexFormatAdd(VERTEX_USAGE_COLOR, VERTEX_TYPE_COLOR); + return RValue_makeReal(0); +} + +// vertex_format_add_position(): adds a 2D position vertex element to the current vertex format. +static RValue builtin_vertex_format_add_position(VMContext* ctx, RValue* args, int32_t argCount) { + vertexFormatAdd(VERTEX_USAGE_POSITION, VERTEX_TYPE_FLOAT2); + return RValue_makeReal(0); +} + +// vertex_format_add_position_3d(): adds a 3D position vertex element to the current vertex format. +static RValue builtin_vertex_format_add_position_3d(VMContext* ctx, RValue* args, int32_t argCount) { + vertexFormatAdd(VERTEX_USAGE_POSITION, VERTEX_TYPE_FLOAT3); + return RValue_makeReal(0); +} + +// vertex_format_add_texcoord(): adds a texture coordinate vertex element to the current vertex format. +static RValue builtin_vertex_format_add_texcoord(VMContext* ctx, RValue* args, int32_t argCount) { + vertexFormatAdd(VERTEX_USAGE_TEXCOORD, VERTEX_TYPE_FLOAT2); + return RValue_makeReal(0); +} + +// vertex_format_add_normal(): adds a normal vertex element to the current vertex format. +static RValue builtin_vertex_format_add_normal(VMContext* ctx, RValue* args, int32_t argCount) { + vertexFormatAdd(VERTEX_USAGE_NORMAL, VERTEX_TYPE_FLOAT3); + return RValue_makeReal(0); +} + +// vertex_format_add_custom(type, usage): adds a custom vertex element to the current vertex format. +static RValue builtin_vertex_format_add_custom(VMContext* ctx, RValue* args, int32_t argCount) { + if (argCount < 2) { + return RValue_makeReal(0); + } + + VertexType type = (VertexType) RValue_toInt32(args[0]); + VertexUsage usage = (VertexUsage) RValue_toInt32(args[1]); + + vertexFormatAdd(usage, type); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_format_end(VMContext* ctx, RValue* args, int32_t argCount) { + printf("vertex_format_end()\n"); + + int id = nextVertexFormatId++; + vertexFormats[id] = buildingFormat; + + building = false; + + return RValue_makeInt32(id); +} + +static RValue builtin_vertex_format_delete(VMContext* ctx, RValue* args, int32_t argCount) { + printf("vertex_format_delete()\n"); + + int id = RValue_toInt32(args[0]); + if (id <= 0 || id >= nextVertexFormatId) { + return RValue_makeReal(0); + } + + vertexFormats[id].numElements = 0; + vertexFormats[id].stride = 0; + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_format_exists(VMContext* ctx, RValue* args, int32_t argCount) { + printf("vertex_format_exists()\n"); + + int id = RValue_toInt32(args[0]); + if (id <= 0 || id >= nextVertexFormatId) { + return RValue_makeBool(false); + } + + return RValue_makeBool(vertexFormats[id].numElements > 0); +} + +static RValue builtin_vertex_format_get_info(VMContext* ctx, RValue* args, int32_t argCount) { + printf("vertex_format_get_info()\n"); + + int id = RValue_toInt32(args[0]); + if (id <= 0 || id >= nextVertexFormatId) { + return RValue_makeUndefined(); + } + + VertexFormat *format = &vertexFormats[id]; + + Instance* ret = Runner_createStruct(ctx->runner); + VM_structSetAndFreeVal(ctx, ret, "num_elements", RValue_makeReal(format->numElements), -1); + VM_structSetAndFreeVal(ctx, ret, "stride", RValue_makeReal(format->stride), -1); + + GMLArray* elements = GMLArray_create(ctx->dataWin->gen8.wadVersion, format->numElements); + for (int i = 0; i < format->numElements; i++) { + VertexElement *e = &format->elements[i]; + Instance* elemStruct = Runner_createStruct(ctx->runner); + VM_structSetAndFreeVal(ctx, elemStruct, "usage", RValue_makeReal(e->usage), -1); + VM_structSetAndFreeVal(ctx, elemStruct, "type", RValue_makeReal(e->type), -1); + VM_structSetAndFreeVal(ctx, elemStruct, "offset", RValue_makeReal(e->offset), -1); + VM_structSetAndFreeVal(ctx, elemStruct, "size", RValue_makeReal(e->size), -1); + *GMLArray_slot(elements, i) = RValue_makeStructAndIncRef(elemStruct); + } + VM_structSetAndFreeVal(ctx, ret, "elements", RValue_makeArray(elements), -1); + + return RValue_makeStructAndIncRef(ret); +} + +int createBuffer(size_t initialCapacity) { + printf("createBuffer(initialCapacity=%zu)\n", initialCapacity); + + VertexBuffer *buffer = (VertexBuffer *)safeMalloc(sizeof(VertexBuffer)); + buffer->data = (uint8_t *)safeMalloc(initialCapacity); + buffer->size = 0; + buffer->capacity = initialCapacity; + buffer->format = 0; + buffer->vertexSize = 0; + buffer->currentOffset = 0; + buffer->isFrozen = false; + buffer->rendererData = NULL; + + int id = nextVertexBufferId++; + vertexBuffers[id] = buffer; + + return id; +} + +static RValue builtin_vertex_create_buffer(VMContext* ctx, RValue* args, int32_t argCount) { + int id = createBuffer(1024); + return RValue_makeInt32(id); +} + +static RValue builtin_vertex_create_buffer_ext(VMContext* ctx, RValue* args, int32_t argCount) { + if (1 < argCount) { + return RValue_makeInt32(-1); + } + + int id = createBuffer((size_t) RValue_toInt32(args[0])); + + return RValue_makeInt32(id); +} + +int createBufferFromBuffer(VertexBuffer *sourceBuffer, size_t newCapacity) { + printf("createBufferFromBuffer(sourceBuffer=%p, newCapacity=%zu)\n", sourceBuffer, newCapacity); + + int id = createBuffer(newCapacity); + VertexBuffer *newBuffer = getVertexBuffer(id); + + memcpy(newBuffer->data, sourceBuffer->data, sourceBuffer->size); + newBuffer->size = sourceBuffer->size; + newBuffer->format = sourceBuffer->format; + newBuffer->vertexSize = sourceBuffer->vertexSize; + + return id; +} + +static RValue builtin_vertex_create_buffer_from_buffer(VMContext* ctx, RValue* args, int32_t argCount) { + if (1 < argCount) { + return RValue_makeInt32(-1); + } + + int sourceId = RValue_toInt32(args[0]); + VertexBuffer *sourceBuffer = getVertexBuffer(sourceId); + + if (sourceBuffer == nullptr) { + return RValue_makeInt32(-1); + } + + int id = createBufferFromBuffer(sourceBuffer, sourceBuffer->capacity); + return RValue_makeInt32(id); +} + +static RValue builtin_vertex_create_buffer_from_buffer_ext(VMContext* ctx, RValue* args, int32_t argCount) { + if (2 < argCount) { + return RValue_makeInt32(-1); + } + + int sourceId = RValue_toInt32(args[0]); + VertexBuffer *sourceBuffer = getVertexBuffer(sourceId); + + if (sourceBuffer == nullptr) { + return RValue_makeInt32(-1); + } + + size_t newCapacity = (size_t) RValue_toInt32(args[1]); + if (newCapacity < sourceBuffer->size) { + newCapacity = sourceBuffer->size; + } + + int id = createBufferFromBuffer(sourceBuffer, newCapacity); + return RValue_makeInt32(id); +} + +static RValue builtin_vertex_update_buffer_from_buffer(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_update_buffer_from_buffer()\n"); + + if (2 < argCount) { + return RValue_makeInt32(-1); + } + + int targetId = RValue_toInt32(args[0]); + VertexBuffer *targetBuffer = getVertexBuffer(targetId); + + int sourceId = RValue_toInt32(args[1]); + VertexBuffer *sourceBuffer = getVertexBuffer(sourceId); + + if (targetBuffer == nullptr || sourceBuffer == nullptr) { + return RValue_makeInt32(-1); + } + + if (targetBuffer->capacity < sourceBuffer->size) { + targetBuffer->capacity = sourceBuffer->size; + targetBuffer->data = (uint8_t *)safeRealloc(targetBuffer->data, targetBuffer->capacity); + } + + memcpy(targetBuffer->data, sourceBuffer->data, sourceBuffer->size); + targetBuffer->size = sourceBuffer->size; + targetBuffer->format = sourceBuffer->format; + targetBuffer->vertexSize = sourceBuffer->vertexSize; + + return RValue_makeInt32(0); +} + +static RValue builtin_vertex_update_buffer_from_vertex(VMContext* ctx, RValue* args, int32_t argCount) { + if (2 < argCount) { + return RValue_makeInt32(-1); + } + + int targetId = RValue_toInt32(args[0]); + VertexBuffer *targetBuffer = getVertexBuffer(targetId); + + if (targetBuffer == nullptr) { + return RValue_makeInt32(-1); + } + + if (targetBuffer->capacity < targetBuffer->size + targetBuffer->vertexSize) { + targetBuffer->capacity *= 2; + targetBuffer->data = (uint8_t *)safeRealloc(targetBuffer->data, targetBuffer->capacity); + } + + memcpy(targetBuffer->data + targetBuffer->size, targetBuffer->currentVertex, targetBuffer->vertexSize); + targetBuffer->size += targetBuffer->vertexSize; + + return RValue_makeInt32(0); +} + +static RValue builtin_vertex_get_buffer_size(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_get_buffer_size()\n"); + + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + + if (vb != nullptr) { + return RValue_makeInt32((int32_t)vb->size); + } + + return RValue_makeInt32(0); +} + +static RValue builtin_vertex_get_number(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_get_number()\n"); + + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + + if (vb != nullptr && vb->vertexSize > 0) { + return RValue_makeInt32((int32_t)(vb->size / vb->vertexSize)); + } + + return RValue_makeInt32(0); +} + +static RValue builtin_vertex_delete_buffer(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_delete_buffer()\n"); + + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + + if (vb != nullptr) { + free(vb->data); + free(vb); + vertexBuffers[id] = nullptr; + } + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_buffer_exists(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_buffer_exists()\n"); + + int id = RValue_toInt32(args[0]); + if (id <= 0 || id >= nextVertexBufferId) { + return RValue_makeBool(false); + } + + return RValue_makeBool(vertexBuffers[id] != nullptr); +} + +static RValue builtin_vertex_begin(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_begin()\n"); + + int vb = RValue_toInt32(args[0]);; + int fmt = RValue_toInt32(args[1]);; + + if (vb <= 0 || vb >= nextVertexBufferId || fmt <= 0 || fmt >= nextVertexFormatId) + return RValue_makeReal(0); + + VertexBuffer *buffer = getVertexBuffer(vb); + if (!buffer || buffer->isFrozen) + return RValue_makeReal(0); + + VertexFormat *format = &vertexFormats[fmt]; + + buffer->format = format; + buffer->vertexSize = format->stride; + + return RValue_makeReal(0); +} + +static bool vertexWrite(VertexBuffer *vb, VertexUsage usage, const void *data, size_t size) { + printf("vertexWrite(usage=%d, size=%zu)\n", usage, size); + + VertexElement *element = findElement(vb->format, usage); + + if (!element) + return false; + + memcpy(vb->currentVertex + element->offset, data, size); + return true; +} + +static void vertexCommit(VertexBuffer *vb) +{ + printf("vertexCommit()\n"); + if (vb->size + vb->format->stride > vb->capacity) + return; + + memcpy(vb->data + vb->size, vb->currentVertex, vb->format->stride); + + vb->size += vb->format->stride; +} + +static void vertexBeginNew(VertexBuffer *vb) +{ + printf("vertexBeginNew()\n"); + if (vb->vertexStarted) { + vertexCommit(vb); + memset(vb->currentVertex, 0, vb->format->stride); + } + + vb->vertexStarted = true; +} + +static VertexBuffer *vertexGetBufferFromArgs(RValue *args) +{ + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + + if (!vb || vb->isFrozen) + return NULL; + + return vb; +} + +static uint32_t convertGMColour(uint32_t colour, float alpha) { + uint8_t r = (colour >> 16) & 0xFF; + uint8_t g = (colour >> 8) & 0xFF; + uint8_t b = colour & 0xFF; + uint8_t a = (uint8_t)(alpha * 255.0f); + return (a << 24) | (b << 16) | (g << 8) | r; +} + +static RValue builtin_vertex_colour_common( + RValue* args, + int32_t argCount, + VertexUsage usage +) { + printf("builtin_vertex_colour_common(usage=%d)\n", usage); + if (argCount < 3) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + uint32_t colour = RValue_toInt32(args[1]); + float alpha = RValue_toReal(args[2]); + + uint32_t packed = convertGMColour(colour, alpha); + + vertexWrite(vb, usage, &packed, sizeof(packed)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_color(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_colour_common(a, n, VERTEX_USAGE_COLOR); +} + +static RValue builtin_vertex_argb(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_colour_common(a, n, VERTEX_USAGE_COLOR); +} + +static RValue builtin_vertex_normal(VMContext* ctx, RValue* args, int32_t argCount) +{ + printf("builtin_vertex_normal()\n"); + if (argCount < 4) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + float data[3] = { + RValue_toReal(args[1]), + RValue_toReal(args[2]), + RValue_toReal(args[3]) + }; + + vertexWrite(vb, VERTEX_USAGE_NORMAL, data, sizeof(data)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_position(VMContext* ctx, RValue* args, int32_t argCount) +{ + printf("builtin_vertex_position()\n"); + if (argCount < 3) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + vertexBeginNew(vb); + + float data[2] = { + RValue_toReal(args[1]), + RValue_toReal(args[2]) + }; + + vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(data)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_position_3d(VMContext* ctx, RValue* args, int32_t argCount) +{ + printf("builtin_vertex_position_3d()\n"); + if (argCount < 4) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + vertexBeginNew(vb); + + float data[3] = { + RValue_toReal(args[1]), + RValue_toReal(args[2]), + RValue_toReal(args[3]) + }; + + vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(data)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_texcoord(VMContext* ctx, RValue* args, int32_t argCount) +{ + printf("builtin_vertex_texcoord()\n"); + if (argCount < 3) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + float data[2] = { + RValue_toReal(args[1]), + RValue_toReal(args[2]) + }; + + vertexWrite(vb, VERTEX_USAGE_TEXCOORD, data, sizeof(data)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_floatN( + VMContext* ctx, + RValue* args, + int32_t argCount, + int count +) { + printf("builtin_vertex_floatN(count=%d)\n", count); + if (argCount < count + 1) + return RValue_makeReal(0); + + VertexBuffer *vb = vertexGetBufferFromArgs(args); + if (!vb) + return RValue_makeReal(0); + + float data[4]; + + for (int i = 0; i < count; i++) + data[i] = RValue_toReal(args[i + 1]); + + vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(float) * count); + + return RValue_makeReal(0); +}static RValue builtin_vertex_float1(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_floatN(c, a, n, 1); +} + +static RValue builtin_vertex_float2(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_floatN(c, a, n, 2); +} + +static RValue builtin_vertex_float3(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_floatN(c, a, n, 3); +} + +static RValue builtin_vertex_float4(VMContext* c, RValue* a, int32_t n) +{ + return builtin_vertex_floatN(c, a, n, 4); +} + +static RValue builtin_vertex_ubyte4(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_ubyte4()\n"); + if (argCount < 5) { + return RValue_makeReal(0); + } + + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + if (!vb || vb->isFrozen) + return RValue_makeReal(0); + + uint8_t x = (uint8_t)RValue_toInt32(args[1]);; + uint8_t y = (uint8_t)RValue_toInt32(args[2]);; + uint8_t z = (uint8_t)RValue_toInt32(args[3]);; + uint8_t w = (uint8_t)RValue_toInt32(args[4]);; + + VertexElement *e = findElement(vb->format, VERTEX_USAGE_POSITION); + memcpy(vb->currentVertex + e->offset, &x, sizeof(uint8_t)); + memcpy(vb->currentVertex + e->offset + sizeof(uint8_t), &y, sizeof(uint8_t)); + memcpy(vb->currentVertex + e->offset + sizeof(uint8_t) * 2, &z, sizeof(uint8_t)); + memcpy(vb->currentVertex + e->offset + sizeof(uint8_t) * 3, &w, sizeof(uint8_t)); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_end(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_end()\n"); + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + if (!vb || vb->isFrozen) + return RValue_makeReal(0); + + if (vb->size + vb->vertexSize > vb->capacity) { + vb->capacity *= 2; + vb->data = (uint8_t *)safeRealloc(vb->data, vb->capacity); + } + memcpy(vb->data + vb->size, vb->currentVertex, vb->vertexSize); + vb->size += vb->vertexSize; + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_freeze(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_freeze()\n"); + int id = RValue_toInt32(args[0]); + VertexBuffer *vb = getVertexBuffer(id); + if (!vb) + return RValue_makeReal(0); + + if (vb->vertexStarted) { + vertexCommit(vb); + vb->vertexStarted = false; + } + + vb->isFrozen = true; + return RValue_makeReal(0); +} + +static RValue builtin_vertex_submit( + VMContext* ctx, + RValue* args, + int32_t argCount +) { + printf("builtin_vertex_submit()\n"); + if (argCount < 3) + return RValue_makeReal(0); + + int id = RValue_toInt32(args[0]); + int primitive = RValue_toInt32(args[1]); + int texture = RValue_toInt32(args[2]); + + VertexBuffer *buffer = getVertexBuffer(id); + + if (!buffer) + return RValue_makeReal(0); + + Renderer* renderer = ctx->runner->renderer; + if (!renderer || !renderer->vtable || !renderer->vtable->drawVertexBuffer) + return RValue_makeReal(0); + + renderer->vtable->drawVertexBuffer(renderer, buffer, primitive, texture); + + return RValue_makeReal(0); +} + +static RValue builtin_vertex_submit_ext(VMContext* ctx, RValue* args, int32_t argCount) { + printf("builtin_vertex_submit_ext()\n"); + // This function would typically submit the vertex buffer to the GPU for rendering with additional parameters. + // In this implementation, we just return 0 to indicate success. + return RValue_makeReal(0); +} + // ===[ REGISTRATION ]=== void VMBuiltins_registerAll(VMContext* ctx) { @@ -17314,6 +18007,48 @@ void VMBuiltins_registerAll(VMContext* ctx) { VM_registerBuiltin(ctx, "mp_grid_draw", builtin_mp_grid_draw); VM_registerBuiltin(ctx, "mp_grid_path", builtin_mp_grid_path); + // Vertex + VM_registerBuiltin(ctx, "vertex_format_begin", builtin_vertex_format_begin); + VM_registerBuiltin(ctx, "vertex_format_add_colour", builtin_vertex_format_add_color); + VM_registerBuiltin(ctx, "vertex_format_add_color", builtin_vertex_format_add_color); + VM_registerBuiltin(ctx, "vertex_format_add_position", builtin_vertex_format_add_position); + VM_registerBuiltin(ctx, "vertex_format_add_position_3d", builtin_vertex_format_add_position_3d); + VM_registerBuiltin(ctx, "vertex_format_add_texcoord", builtin_vertex_format_add_texcoord); + VM_registerBuiltin(ctx, "vertex_format_add_normal", builtin_vertex_format_add_normal); + VM_registerBuiltin(ctx, "vertex_format_add_custom", builtin_vertex_format_add_custom); + VM_registerBuiltin(ctx, "vertex_format_end", builtin_vertex_format_end); + VM_registerBuiltin(ctx, "vertex_format_delete", builtin_vertex_format_delete); + VM_registerBuiltin(ctx, "vertex_format_exists", builtin_vertex_format_exists); + VM_registerBuiltin(ctx, "vertex_format_get_info", builtin_vertex_format_get_info); + + VM_registerBuiltin(ctx, "vertex_create_buffer", builtin_vertex_create_buffer); + VM_registerBuiltin(ctx, "vertex_create_buffer_ext", builtin_vertex_create_buffer_ext); + VM_registerBuiltin(ctx, "vertex_create_buffer_from_buffer", builtin_vertex_create_buffer_from_buffer); + VM_registerBuiltin(ctx, "vertex_create_buffer_from_buffer_ext", builtin_vertex_create_buffer_from_buffer_ext); + VM_registerBuiltin(ctx, "vertex_update_buffer_from_buffer", builtin_vertex_update_buffer_from_buffer); + VM_registerBuiltin(ctx, "vertex_update_buffer_from_vertex", builtin_vertex_update_buffer_from_vertex); + VM_registerBuiltin(ctx, "vertex_get_buffer_size", builtin_vertex_get_buffer_size); + VM_registerBuiltin(ctx, "vertex_get_number", builtin_vertex_get_number); + VM_registerBuiltin(ctx, "vertex_delete_buffer", builtin_vertex_delete_buffer); + VM_registerBuiltin(ctx, "vertex_buffer_exists", builtin_vertex_buffer_exists); + VM_registerBuiltin(ctx, "vertex_begin", builtin_vertex_begin); + VM_registerBuiltin(ctx, "vertex_color", builtin_vertex_color); + VM_registerBuiltin(ctx, "vertex_colour", builtin_vertex_color); + VM_registerBuiltin(ctx, "vertex_normal", builtin_vertex_normal); + VM_registerBuiltin(ctx, "vertex_position", builtin_vertex_position); + VM_registerBuiltin(ctx, "vertex_position_3d", builtin_vertex_position_3d); + VM_registerBuiltin(ctx, "vertex_argb", builtin_vertex_argb); + VM_registerBuiltin(ctx, "vertex_texcoord", builtin_vertex_texcoord); + VM_registerBuiltin(ctx, "vertex_float1", builtin_vertex_float1); + VM_registerBuiltin(ctx, "vertex_float2", builtin_vertex_float2); + VM_registerBuiltin(ctx, "vertex_float3", builtin_vertex_float3); + VM_registerBuiltin(ctx, "vertex_float4", builtin_vertex_float4); + VM_registerBuiltin(ctx, "vertex_ubyte4", builtin_vertex_ubyte4); + VM_registerBuiltin(ctx, "vertex_end", builtin_vertex_end); + VM_registerBuiltin(ctx, "vertex_freeze", builtin_vertex_freeze); + VM_registerBuiltin(ctx, "vertex_submit", builtin_vertex_submit); + VM_registerBuiltin(ctx, "vertex_submit_ext", builtin_vertex_submit_ext); + // Misc VM_registerBuiltin(ctx, "get_timer", builtin_get_timer); if (!isGMS2) { From 0d3d4aca54d29c04b395f5990c0ac84d7663db84 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 16:45:58 +0100 Subject: [PATCH 02/12] allow vertex id to be 0 --- src/vm_builtins.c | 58 ++++++++--------------------------------------- 1 file changed, 10 insertions(+), 48 deletions(-) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index df38b0174..bcbf35e5b 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -16363,14 +16363,14 @@ static RValue builtin_sprite_get_info(VMContext* ctx, RValue* args, int32_t argC static VertexFormat buildingFormat; static bool building = false; -static int nextVertexFormatId = 1; +static int nextVertexFormatId = 0; static VertexFormat vertexFormats[256]; -static int nextVertexBufferId = 1; +static int nextVertexBufferId = 0; static VertexBuffer *vertexBuffers[256]; VertexBuffer *getVertexBuffer(int id) { - if (id <= 0 || id >= nextVertexBufferId) { + if (id < 0 || id >= nextVertexBufferId) { return nullptr; } VertexBuffer *buffer = vertexBuffers[id]; @@ -16388,7 +16388,6 @@ static VertexElement *findElement(VertexFormat *format, VertexUsage usage) { // vertex_format_begin(): begins building a new vertex format. static RValue builtin_vertex_format_begin(VMContext* ctx, RValue* args, int32_t argCount) { - printf("vertex_format_begin()\n"); memset(&buildingFormat, 0, sizeof(buildingFormat)); building = true; @@ -16396,7 +16395,6 @@ static RValue builtin_vertex_format_begin(VMContext* ctx, RValue* args, int32_t } static void vertexFormatAdd(VertexUsage usage,VertexType type) { - printf("vertexFormatAdd(usage=%d, type=%d)\n", usage, type); VertexElement *e = &buildingFormat.elements[buildingFormat.numElements++]; e->usage = usage; @@ -16470,8 +16468,6 @@ static RValue builtin_vertex_format_add_custom(VMContext* ctx, RValue* args, int } static RValue builtin_vertex_format_end(VMContext* ctx, RValue* args, int32_t argCount) { - printf("vertex_format_end()\n"); - int id = nextVertexFormatId++; vertexFormats[id] = buildingFormat; @@ -16481,10 +16477,8 @@ static RValue builtin_vertex_format_end(VMContext* ctx, RValue* args, int32_t ar } static RValue builtin_vertex_format_delete(VMContext* ctx, RValue* args, int32_t argCount) { - printf("vertex_format_delete()\n"); - int id = RValue_toInt32(args[0]); - if (id <= 0 || id >= nextVertexFormatId) { + if (id < 0 || id >= nextVertexFormatId) { return RValue_makeReal(0); } @@ -16495,10 +16489,8 @@ static RValue builtin_vertex_format_delete(VMContext* ctx, RValue* args, int32_t } static RValue builtin_vertex_format_exists(VMContext* ctx, RValue* args, int32_t argCount) { - printf("vertex_format_exists()\n"); - int id = RValue_toInt32(args[0]); - if (id <= 0 || id >= nextVertexFormatId) { + if (id < 0 || id >= nextVertexFormatId) { return RValue_makeBool(false); } @@ -16506,10 +16498,8 @@ static RValue builtin_vertex_format_exists(VMContext* ctx, RValue* args, int32_t } static RValue builtin_vertex_format_get_info(VMContext* ctx, RValue* args, int32_t argCount) { - printf("vertex_format_get_info()\n"); - int id = RValue_toInt32(args[0]); - if (id <= 0 || id >= nextVertexFormatId) { + if (id < 0 || id >= nextVertexFormatId) { return RValue_makeUndefined(); } @@ -16535,8 +16525,6 @@ static RValue builtin_vertex_format_get_info(VMContext* ctx, RValue* args, int32 } int createBuffer(size_t initialCapacity) { - printf("createBuffer(initialCapacity=%zu)\n", initialCapacity); - VertexBuffer *buffer = (VertexBuffer *)safeMalloc(sizeof(VertexBuffer)); buffer->data = (uint8_t *)safeMalloc(initialCapacity); buffer->size = 0; @@ -16569,8 +16557,6 @@ static RValue builtin_vertex_create_buffer_ext(VMContext* ctx, RValue* args, int } int createBufferFromBuffer(VertexBuffer *sourceBuffer, size_t newCapacity) { - printf("createBufferFromBuffer(sourceBuffer=%p, newCapacity=%zu)\n", sourceBuffer, newCapacity); - int id = createBuffer(newCapacity); VertexBuffer *newBuffer = getVertexBuffer(id); @@ -16620,8 +16606,6 @@ static RValue builtin_vertex_create_buffer_from_buffer_ext(VMContext* ctx, RValu } static RValue builtin_vertex_update_buffer_from_buffer(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_update_buffer_from_buffer()\n"); - if (2 < argCount) { return RValue_makeInt32(-1); } @@ -16673,8 +16657,6 @@ static RValue builtin_vertex_update_buffer_from_vertex(VMContext* ctx, RValue* a } static RValue builtin_vertex_get_buffer_size(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_get_buffer_size()\n"); - int id = RValue_toInt32(args[0]); VertexBuffer *vb = getVertexBuffer(id); @@ -16686,8 +16668,6 @@ static RValue builtin_vertex_get_buffer_size(VMContext* ctx, RValue* args, int32 } static RValue builtin_vertex_get_number(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_get_number()\n"); - int id = RValue_toInt32(args[0]); VertexBuffer *vb = getVertexBuffer(id); @@ -16699,8 +16679,6 @@ static RValue builtin_vertex_get_number(VMContext* ctx, RValue* args, int32_t ar } static RValue builtin_vertex_delete_buffer(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_delete_buffer()\n"); - int id = RValue_toInt32(args[0]); VertexBuffer *vb = getVertexBuffer(id); @@ -16714,10 +16692,8 @@ static RValue builtin_vertex_delete_buffer(VMContext* ctx, RValue* args, int32_t } static RValue builtin_vertex_buffer_exists(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_buffer_exists()\n"); - int id = RValue_toInt32(args[0]); - if (id <= 0 || id >= nextVertexBufferId) { + if (id < 0 || id >= nextVertexBufferId) { return RValue_makeBool(false); } @@ -16725,12 +16701,10 @@ static RValue builtin_vertex_buffer_exists(VMContext* ctx, RValue* args, int32_t } static RValue builtin_vertex_begin(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_begin()\n"); - - int vb = RValue_toInt32(args[0]);; - int fmt = RValue_toInt32(args[1]);; + int vb = RValue_toInt32(args[0]); + int fmt = RValue_toInt32(args[1]); - if (vb <= 0 || vb >= nextVertexBufferId || fmt <= 0 || fmt >= nextVertexFormatId) + if (vb < 0 || vb >= nextVertexBufferId || fmt < 0 || fmt >= nextVertexFormatId) return RValue_makeReal(0); VertexBuffer *buffer = getVertexBuffer(vb); @@ -16746,8 +16720,6 @@ static RValue builtin_vertex_begin(VMContext* ctx, RValue* args, int32_t argCoun } static bool vertexWrite(VertexBuffer *vb, VertexUsage usage, const void *data, size_t size) { - printf("vertexWrite(usage=%d, size=%zu)\n", usage, size); - VertexElement *element = findElement(vb->format, usage); if (!element) @@ -16759,7 +16731,6 @@ static bool vertexWrite(VertexBuffer *vb, VertexUsage usage, const void *data, s static void vertexCommit(VertexBuffer *vb) { - printf("vertexCommit()\n"); if (vb->size + vb->format->stride > vb->capacity) return; @@ -16770,7 +16741,6 @@ static void vertexCommit(VertexBuffer *vb) static void vertexBeginNew(VertexBuffer *vb) { - printf("vertexBeginNew()\n"); if (vb->vertexStarted) { vertexCommit(vb); memset(vb->currentVertex, 0, vb->format->stride); @@ -16803,7 +16773,6 @@ static RValue builtin_vertex_colour_common( int32_t argCount, VertexUsage usage ) { - printf("builtin_vertex_colour_common(usage=%d)\n", usage); if (argCount < 3) return RValue_makeReal(0); @@ -16833,7 +16802,6 @@ static RValue builtin_vertex_argb(VMContext* c, RValue* a, int32_t n) static RValue builtin_vertex_normal(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_normal()\n"); if (argCount < 4) return RValue_makeReal(0); @@ -16854,7 +16822,6 @@ static RValue builtin_vertex_normal(VMContext* ctx, RValue* args, int32_t argCou static RValue builtin_vertex_position(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_position()\n"); if (argCount < 3) return RValue_makeReal(0); @@ -16876,7 +16843,6 @@ static RValue builtin_vertex_position(VMContext* ctx, RValue* args, int32_t argC static RValue builtin_vertex_position_3d(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_position_3d()\n"); if (argCount < 4) return RValue_makeReal(0); @@ -16899,7 +16865,6 @@ static RValue builtin_vertex_position_3d(VMContext* ctx, RValue* args, int32_t a static RValue builtin_vertex_texcoord(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_texcoord()\n"); if (argCount < 3) return RValue_makeReal(0); @@ -16923,7 +16888,6 @@ static RValue builtin_vertex_floatN( int32_t argCount, int count ) { - printf("builtin_vertex_floatN(count=%d)\n", count); if (argCount < count + 1) return RValue_makeReal(0); @@ -16960,7 +16924,6 @@ static RValue builtin_vertex_float4(VMContext* c, RValue* a, int32_t n) } static RValue builtin_vertex_ubyte4(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_ubyte4()\n"); if (argCount < 5) { return RValue_makeReal(0); } @@ -17002,7 +16965,6 @@ static RValue builtin_vertex_end(VMContext* ctx, RValue* args, int32_t argCount) } static RValue builtin_vertex_freeze(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_freeze()\n"); int id = RValue_toInt32(args[0]); VertexBuffer *vb = getVertexBuffer(id); if (!vb) From 654f7bcc232c368fb71ca1b344641f20bd755109 Mon Sep 17 00:00:00 2001 From: emiyl Date: Fri, 24 Jul 2026 18:07:06 +0100 Subject: [PATCH 03/12] fix(vm): stabilize CoW array writes before fork --- src/vm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vm.c b/src/vm.c index 63000f27a..e3827f142 100644 --- a/src/vm.c +++ b/src/vm.c @@ -262,6 +262,8 @@ static GMLArray* VM_arraySetWithCoW(VMContext* ctx, RValue* slot, int32_t index, require(slot != nullptr); requireMessageFormatted(__FILE__, __LINE__, index >= 0, "Trying to write to an array using a negative index! Index: %d", index); + RValue writeVal = RValue_makeIndependent(val); + void* intendedOwner; #if IS_WAD17_OR_HIGHER_ENABLED intendedOwner = IS_WAD17_OR_HIGHER(ctx) ? ctx->currentArrayOwner : (void*) slot; @@ -277,7 +279,7 @@ static GMLArray* VM_arraySetWithCoW(VMContext* ctx, RValue* slot, int32_t index, fresh->owner = intendedOwner; *slot = RValue_makeArray(fresh); GMLArray_growTo(fresh, index + 1); - GMLArray_set(fresh, index, val); + RValue_writeIntoSlotStealingOwnershipOrCopying(GMLArray_slot(fresh, index), writeVal); return fresh; } @@ -299,7 +301,8 @@ static GMLArray* VM_arraySetWithCoW(VMContext* ctx, RValue* slot, int32_t index, } // Case 3: Write! - GMLArray_set(arr, index, val); + GMLArray_growTo(arr, index + 1); + RValue_writeIntoSlotStealingOwnershipOrCopying(GMLArray_slot(arr, index), writeVal); return arr; } From dd1cfec628751b05bf01a235481272449cfa2e1a Mon Sep 17 00:00:00 2001 From: Emma Date: Sat, 25 Jul 2026 13:49:20 +0100 Subject: [PATCH 04/12] fix(layers): Use strcasecmp instead of strcmp to resolve layers (#348) * fix(vm): use strcasecmp to resolve layers * add more strcasecmp and null checks --- src/vm_builtins.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index bcbf35e5b..431937490 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -12996,13 +12996,13 @@ static int32_t resolveLayerIdArg(Runner* runner, RValue arg) { size_t runtimeLayerCount = arrlenu(runner->runtimeLayers); repeat(runtimeLayerCount, i) { RuntimeLayer* rl = &runner->runtimeLayers[i]; - if (rl->dynamic && strcmp(rl->dynamicName, name) == 0) + if (rl->dynamic && rl->dynamicName != nullptr && strcasecmp(rl->dynamicName, name) == 0) return (int32_t) rl->id; } if (runner->currentRoom != nullptr) { repeat(runner->currentRoom->layerCount, i) { RoomLayer* layer = &runner->currentRoom->layers[i]; - if (layer->name != nullptr && strcmp(layer->name, name) == 0) { + if (layer->name != nullptr && strcasecmp(layer->name, name) == 0) { // Only resolve room-layer names that still exist in the runtime layer list. if (Runner_findRuntimeLayerById(runner, (int32_t) layer->id) != nullptr) return (int32_t) layer->id; @@ -13062,7 +13062,7 @@ static RValue builtin_layer_get_id(VMContext* ctx, RValue* args, MAYBE_UNUSED in size_t runtimeLayerCount = arrlenu(runner->runtimeLayers); repeat(runtimeLayerCount, i) { RuntimeLayer* runtimeLayer = &runner->runtimeLayers[i]; - if (runtimeLayer->dynamic && strcmp(runtimeLayer->dynamicName, name) == 0) { + if (runtimeLayer->dynamic && runtimeLayer->dynamicName != nullptr && strcasecmp(runtimeLayer->dynamicName, name) == 0) { result = (int32_t) runtimeLayer->id; break; } @@ -13070,13 +13070,12 @@ static RValue builtin_layer_get_id(VMContext* ctx, RValue* args, MAYBE_UNUSED in if (result == -1 && runner->currentRoom != nullptr) { repeat(runner->currentRoom->layerCount, i) { RoomLayer* layer = &runner->currentRoom->layers[i]; - if (layer->name != nullptr && strcmp(layer->name, name) == 0) { + if (layer->name != nullptr && strcasecmp(layer->name, name) == 0) { result = (int32_t) layer->id; break; } } } - free(name); return RValue_makeReal((GMLReal) result); } From 95ba8e85d6a7d952e2ce14e241df168e407b2c6e Mon Sep 17 00:00:00 2001 From: Cobalt <65132371+cobaltgit@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:59:54 +0100 Subject: [PATCH 05/12] perf(audio): mmap audiogroup*.dat if main data.win is being mapped (#352) * perf(audio): mmap audiogroup*.dat if main data.win is being mapped * fix(openal): assign datawin * what --- src/audio/miniaudio/ma_audio_system.c | 2 ++ src/audio/openal/al_audio_system.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/audio/miniaudio/ma_audio_system.c b/src/audio/miniaudio/ma_audio_system.c index 9c8a146c9..07522dfd6 100644 --- a/src/audio/miniaudio/ma_audio_system.c +++ b/src/audio/miniaudio/ma_audio_system.c @@ -750,6 +750,8 @@ static void maGroupLoad(AudioSystem* audio, int32_t groupIndex) { DataWinParserOptions options = {0}; options.parseAudo = true; + if (audio->dw->mappedFile) + options.loadType = DATAWINLOADTYPE_MAP_FILE; DataWin *audioGroup = DataWin_parse(((MaAudioSystem*)audio)->fileSystem->vtable->resolvePath(((MaAudioSystem*)audio)->fileSystem, buf), options); arrput(audio->audioGroups, audioGroup); free(buf); diff --git a/src/audio/openal/al_audio_system.c b/src/audio/openal/al_audio_system.c index c90604142..70953259b 100644 --- a/src/audio/openal/al_audio_system.c +++ b/src/audio/openal/al_audio_system.c @@ -163,6 +163,7 @@ static char* resolveExternalPath(AlAudioSystem* ma, Sound* sound) { static void maInit(AudioSystem* audio, DataWin* dataWin, FileSystem* fileSystem) { AlAudioSystem* ma = (AlAudioSystem*) audio; + ma->base.dw = dataWin; arrput(ma->base.audioGroups, dataWin); ma->fileSystem = fileSystem; @@ -945,6 +946,8 @@ static void maGroupLoad(AudioSystem* audio, int32_t groupIndex) { DataWinParserOptions options = {0}; options.parseAudo = true; + if (audio->dw->mappedFile) + options.loadType = DATAWINLOADTYPE_MAP_FILE; DataWin *audioGroup = DataWin_parse(((AlAudioSystem*)audio)->fileSystem->vtable->resolvePath(((AlAudioSystem*)audio)->fileSystem, buf), options); arrput(audio->audioGroups, audioGroup); free(buf); From fce752771a94c80920beeec9e7f5d4dbf30fc804 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 25 Jul 2026 09:15:29 -0400 Subject: [PATCH 06/12] Make fast forward bools non-static (#354) Before, the fast-forward state would be saved across game launches on stuff like iOS where a return from main doesn't mean the process ends. --- src/desktop/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/desktop/main.c b/src/desktop/main.c index 90635ae8d..01f7828e5 100644 --- a/src/desktop/main.c +++ b/src/desktop/main.c @@ -1016,6 +1016,8 @@ int main(int argc, char* argv[]) { bool platformInitialized = false; int32_t inputFrameCount = 0; + bool fastForwardActive = false; + bool fastForwardTabPrev = false; while (true) { fprintf(stderr, "Loading %s...\n", args.dataWinPath); @@ -1803,8 +1805,6 @@ int main(int argc, char* argv[]) { // Limit frame rate to room speed (skip in headless mode for max speed!!) if (!args.headless && runner->currentRoom->speed > 0) { - static bool fastForwardActive = false; - static bool fastForwardTabPrev = false; bool fastForwardTabNow = RunnerKeyboard_checkPressed(runner->keyboard, VK_TAB); if (args.fastForwardSpeed > 0.0 && fastForwardTabNow && !fastForwardTabPrev) { fastForwardActive = !fastForwardActive; From c6789573a048ede2ebd4ce91bb14d6bca211c033 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 17:33:48 +0100 Subject: [PATCH 07/12] dynamic arrays of buffers and formats --- src/gl/gl_renderer.c | 3 +- src/vm_builtins.c | 172 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index ced1b3c4f..665feb499 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1610,7 +1610,7 @@ static int vertex_type_components(int type) return 4; } -static void glDrawVertexBuffer(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture) { +static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture) { if (!buffer || !buffer->format) return; @@ -1719,7 +1719,6 @@ static void glDrawVertexBuffer(Renderer* renderer, VertexBuffer* buffer, int32_t } uint32_t vertexCount = buffer->size / buffer->format->stride; - printf("Drawing vertex buffer with %u vertices\n", vertexCount); glDrawArrays( mode, 0, diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 431937490..1d410e242 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -16363,10 +16363,119 @@ static VertexFormat buildingFormat; static bool building = false; static int nextVertexFormatId = 0; -static VertexFormat vertexFormats[256]; +static VertexFormat **vertexFormats = nullptr; +static int vertexFormatCapacity = 0; static int nextVertexBufferId = 0; -static VertexBuffer *vertexBuffers[256]; +static VertexBuffer **vertexBuffers = nullptr; +static int vertexBufferCapacity = 0; + +static int *freeVertexFormatIds = nullptr; +static int freeVertexFormatCount = 0; +static int freeVertexFormatCapacity = 0; + +static int *freeVertexBufferIds = nullptr; +static int freeVertexBufferCount = 0; +static int freeVertexBufferCapacity = 0; + +#define RESOURCE_VERTEX_FORMAT 0x01000000 + +static int getNextVertexFormatId() { + int id = nextVertexFormatId++; + return RESOURCE_VERTEX_FORMAT | id; +} + +static int getNextVertexBufferId() { + return nextVertexBufferId++; +} + +static void ensureListCapacity(int capacity, int **list, int *listCapacity) { + if (capacity < *listCapacity) { + return; + } + + int newCapacity = *listCapacity ? *listCapacity * 2 : 16; + + while (newCapacity <= capacity) + newCapacity *= 2; + + int *newList = (int *)safeMalloc(newCapacity * sizeof(int)); + + if (*list != NULL) { + memcpy(newList, *list, *listCapacity * sizeof(int)); + free(*list); + } + + for (int i = *listCapacity; i < newCapacity; i++) { + newList[i] = 0; + } + + *list = newList; + *listCapacity = newCapacity; +} + +static void addFreeId(int id, int **list, int *listCount, int *listCapacity) { + if (id < 0) { + return; + } + + if (*listCount >= *listCapacity) { + int newCapacity = *listCapacity ? *listCapacity * 2 : 16; + + int *newList = (int *)safeMalloc(newCapacity * sizeof(int)); + + if (*list != NULL) { + memcpy(newList, *list, *listCount * sizeof(int)); + free(*list); + } + + *list = newList; + *listCapacity = newCapacity; + } + + (*list)[(*listCount)++] = id; +} + +static void ensureVertexFormatCapacity(int capacity) { + ensureListCapacity(capacity, (int **)&vertexFormats, &vertexFormatCapacity); +} + +static void ensureVertexBufferCapacity(int capacity) { + ensureListCapacity(capacity, (int **)&vertexBuffers, &vertexBufferCapacity); +} + +static void freeVertexFormat(int id) { + int local_id = id & 0x00FFFFFF; + if (local_id < 0 || local_id >= nextVertexFormatId) { + return; + } + + VertexFormat *format = vertexFormats[local_id]; + + if (format != nullptr) { + free(format->elements); + free(format); + vertexFormats[local_id] = nullptr; + } + + addFreeId(local_id, &freeVertexFormatIds, &freeVertexFormatCount, &freeVertexFormatCapacity); +} + +static void freeVertexBuffer(int id) { + if (id < 0 || id >= nextVertexBufferId) { + return; + } + + VertexBuffer *buffer = vertexBuffers[id]; + + if (buffer != nullptr) { + free(buffer->data); + free(buffer); + vertexBuffers[id] = nullptr; + } + + addFreeId(id, &freeVertexBufferIds, &freeVertexBufferCount, &freeVertexBufferCapacity); +} VertexBuffer *getVertexBuffer(int id) { if (id < 0 || id >= nextVertexBufferId) { @@ -16467,8 +16576,19 @@ static RValue builtin_vertex_format_add_custom(VMContext* ctx, RValue* args, int } static RValue builtin_vertex_format_end(VMContext* ctx, RValue* args, int32_t argCount) { - int id = nextVertexFormatId++; - vertexFormats[id] = buildingFormat; + int id; + // // I thought a dynamic system would be nice but apparently GameMaker doesn't do that + // // The IDs just increment and never get reused + // if (freeVertexFormatCount > 0) { + // id = freeVertexFormatIds[--freeVertexFormatCount]; + // } else { + id = getNextVertexFormatId(); + int local_id = id & 0x00FFFFFF; + ensureVertexFormatCapacity(local_id); + // } + + vertexFormats[local_id] = (VertexFormat *)safeMalloc(sizeof(VertexFormat)); + memcpy(vertexFormats[local_id], &buildingFormat, sizeof(VertexFormat)); building = false; @@ -16477,32 +16597,34 @@ static RValue builtin_vertex_format_end(VMContext* ctx, RValue* args, int32_t ar static RValue builtin_vertex_format_delete(VMContext* ctx, RValue* args, int32_t argCount) { int id = RValue_toInt32(args[0]); - if (id < 0 || id >= nextVertexFormatId) { + int local_id = id & 0x00FFFFFF; + if (local_id < 0 || local_id >= nextVertexFormatId) { return RValue_makeReal(0); } - vertexFormats[id].numElements = 0; - vertexFormats[id].stride = 0; + freeVertexFormat(local_id); return RValue_makeReal(0); } static RValue builtin_vertex_format_exists(VMContext* ctx, RValue* args, int32_t argCount) { int id = RValue_toInt32(args[0]); - if (id < 0 || id >= nextVertexFormatId) { + int local_id = id & 0x00FFFFFF; + if (local_id < 0 || local_id >= nextVertexFormatId) { return RValue_makeBool(false); } - return RValue_makeBool(vertexFormats[id].numElements > 0); + return RValue_makeBool(vertexFormats[local_id]->numElements > 0); } static RValue builtin_vertex_format_get_info(VMContext* ctx, RValue* args, int32_t argCount) { int id = RValue_toInt32(args[0]); - if (id < 0 || id >= nextVertexFormatId) { + int local_id = id & 0x00FFFFFF; + if (local_id < 0 || local_id >= nextVertexFormatId) { return RValue_makeUndefined(); } - VertexFormat *format = &vertexFormats[id]; + VertexFormat *format = vertexFormats[local_id]; Instance* ret = Runner_createStruct(ctx->runner); VM_structSetAndFreeVal(ctx, ret, "num_elements", RValue_makeReal(format->numElements), -1); @@ -16525,6 +16647,7 @@ static RValue builtin_vertex_format_get_info(VMContext* ctx, RValue* args, int32 int createBuffer(size_t initialCapacity) { VertexBuffer *buffer = (VertexBuffer *)safeMalloc(sizeof(VertexBuffer)); + buffer->data = (uint8_t *)safeMalloc(initialCapacity); buffer->size = 0; buffer->capacity = initialCapacity; @@ -16534,7 +16657,17 @@ int createBuffer(size_t initialCapacity) { buffer->isFrozen = false; buffer->rendererData = NULL; - int id = nextVertexBufferId++; + int id; + + // // I thought a dynamic system would be nice but apparently GameMaker doesn't do that + // // The IDs just increment and never get reused + // if (freeVertexBufferCount > 0) { + // id = freeVertexBufferIds[--freeVertexBufferCount]; + // } else { + id = getNextVertexBufferId(); + ensureVertexBufferCapacity(id); + // } + vertexBuffers[id] = buffer; return id; @@ -16679,13 +16812,7 @@ static RValue builtin_vertex_get_number(VMContext* ctx, RValue* args, int32_t ar static RValue builtin_vertex_delete_buffer(VMContext* ctx, RValue* args, int32_t argCount) { int id = RValue_toInt32(args[0]); - VertexBuffer *vb = getVertexBuffer(id); - - if (vb != nullptr) { - free(vb->data); - free(vb); - vertexBuffers[id] = nullptr; - } + freeVertexBuffer(id); return RValue_makeReal(0); } @@ -16701,16 +16828,16 @@ static RValue builtin_vertex_buffer_exists(VMContext* ctx, RValue* args, int32_t static RValue builtin_vertex_begin(VMContext* ctx, RValue* args, int32_t argCount) { int vb = RValue_toInt32(args[0]); - int fmt = RValue_toInt32(args[1]); + int fmt = RValue_toInt32(args[1]) & 0x00FFFFFF; - if (vb < 0 || vb >= nextVertexBufferId || fmt < 0 || fmt >= nextVertexFormatId) + if (vb < 0 || vb >= nextVertexBufferId || fmt < 0 || (fmt) >= nextVertexFormatId) return RValue_makeReal(0); VertexBuffer *buffer = getVertexBuffer(vb); if (!buffer || buffer->isFrozen) return RValue_makeReal(0); - VertexFormat *format = &vertexFormats[fmt]; + VertexFormat *format = vertexFormats[fmt]; buffer->format = format; buffer->vertexSize = format->stride; @@ -16983,7 +17110,6 @@ static RValue builtin_vertex_submit( RValue* args, int32_t argCount ) { - printf("builtin_vertex_submit()\n"); if (argCount < 3) return RValue_makeReal(0); From 59037850c0f11a473fd27a3776720cc4f72f8841 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 17:36:43 +0100 Subject: [PATCH 08/12] fix double free --- src/vm_builtins.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 1d410e242..9f98a56db 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -16453,7 +16453,6 @@ static void freeVertexFormat(int id) { VertexFormat *format = vertexFormats[local_id]; if (format != nullptr) { - free(format->elements); free(format); vertexFormats[local_id] = nullptr; } @@ -16469,7 +16468,6 @@ static void freeVertexBuffer(int id) { VertexBuffer *buffer = vertexBuffers[id]; if (buffer != nullptr) { - free(buffer->data); free(buffer); vertexBuffers[id] = nullptr; } From 2241479ff8e16ca0a4c8c92b46f7ba9e0c772953 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 17:42:48 +0100 Subject: [PATCH 09/12] shh compiler --- src/gl/gl_renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 665feb499..0a621830e 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1618,12 +1618,12 @@ static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* bu GLuint vbo; } GLVertexBuffer; - GLVertexBuffer *glBuffer = buffer->rendererData; + GLVertexBuffer *glBuffer = (GLVertexBuffer *)buffer->rendererData; if (!glBuffer) { glBuffer = malloc(sizeof(GLVertexBuffer)); glGenBuffers(1, &glBuffer->vbo); - buffer->rendererData = glBuffer; + buffer->rendererData = (void *) glBuffer; } GLenum mode; From ad28aca7953e121592499d45ada398f0246a38d0 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 17:50:11 +0100 Subject: [PATCH 10/12] add vertex_submit_ext --- src/gl/gl_renderer.c | 20 ++++++++++++++++---- src/renderer.h | 2 +- src/vm_builtins.c | 32 +++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 0a621830e..565f6c6da 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1610,7 +1610,7 @@ static int vertex_type_components(int type) return 4; } -static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture) { +static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture, int32_t offset, int32_t number) { if (!buffer || !buffer->format) return; @@ -1718,11 +1718,23 @@ static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* bu glBindTexture(GL_TEXTURE_2D, texture); } - uint32_t vertexCount = buffer->size / buffer->format->stride; + int vertexCount = buffer->size / buffer->format->stride; + if (offset < 0) offset = 0; + if (offset >= vertexCount) offset = vertexCount - 1; + + if (number == -1) { + number = vertexCount - offset; + } else { + if (number < 0) number = 0; + if (offset + number > vertexCount) { + number = vertexCount - offset; + } + } + glDrawArrays( mode, - 0, - vertexCount + offset, + number ); for (int i = 0; i < buffer->format->numElements; i++) { diff --git a/src/renderer.h b/src/renderer.h index 6d0be397a..84fa88b92 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -156,7 +156,7 @@ typedef struct { void (*drawLineColor)(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha); void (*drawText)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation); void (*drawTextColor)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, float lineSeparation); - void (*drawVertexBuffer)(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture); + void (*drawVertexBuffer)(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture, int32_t offset, int32_t count); void (*flush)(Renderer* renderer); void (*clearScreen)(Renderer* renderer, uint32_t color, float alpha); int32_t (*createSpriteFromSurface)(Renderer* renderer, int32_t surfaceID, int32_t x, int32_t y, int32_t w, int32_t h, bool removeback, bool smooth, int32_t xorig, int32_t yorig); diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 9f98a56db..6fa8e010e 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -17103,11 +17103,7 @@ static RValue builtin_vertex_freeze(VMContext* ctx, RValue* args, int32_t argCou return RValue_makeReal(0); } -static RValue builtin_vertex_submit( - VMContext* ctx, - RValue* args, - int32_t argCount -) { +static RValue builtin_vertex_submit(VMContext* ctx, RValue* args,int32_t argCount) { if (argCount < 3) return RValue_makeReal(0); @@ -17124,15 +17120,33 @@ static RValue builtin_vertex_submit( if (!renderer || !renderer->vtable || !renderer->vtable->drawVertexBuffer) return RValue_makeReal(0); - renderer->vtable->drawVertexBuffer(renderer, buffer, primitive, texture); + uint32_t vertexCount = buffer->size / buffer->format->stride; + renderer->vtable->drawVertexBuffer(renderer, buffer, primitive, texture, 0, vertexCount); return RValue_makeReal(0); } static RValue builtin_vertex_submit_ext(VMContext* ctx, RValue* args, int32_t argCount) { - printf("builtin_vertex_submit_ext()\n"); - // This function would typically submit the vertex buffer to the GPU for rendering with additional parameters. - // In this implementation, we just return 0 to indicate success. + if (argCount < 5) + return RValue_makeReal(0); + + int id = RValue_toInt32(args[0]); + int primitive = RValue_toInt32(args[1]); + int texture = RValue_toInt32(args[2]); + int offset = (int)RValue_toReal(args[3]); + int count = (int)RValue_toReal(args[4]); + + VertexBuffer *buffer = getVertexBuffer(id); + + if (!buffer) + return RValue_makeReal(0); + + Renderer* renderer = ctx->runner->renderer; + if (!renderer || !renderer->vtable || !renderer->vtable->drawVertexBuffer) + return RValue_makeReal(0); + + renderer->vtable->drawVertexBuffer(renderer, buffer, primitive, texture, offset, count); + return RValue_makeReal(0); } From f7379eaf3faba184bca3f238899f54cc619dc4d5 Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 17:53:10 +0100 Subject: [PATCH 11/12] shush compiler --- src/gl/gl_renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 565f6c6da..f100f4b17 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1621,7 +1621,7 @@ static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* bu GLVertexBuffer *glBuffer = (GLVertexBuffer *)buffer->rendererData; if (!glBuffer) { - glBuffer = malloc(sizeof(GLVertexBuffer)); + glBuffer = (GLVertexBuffer *)malloc(sizeof(*glBuffer)); glGenBuffers(1, &glBuffer->vbo); buffer->rendererData = (void *) glBuffer; } From deb41b79c2573591151f80fdce1b6730c995d75b Mon Sep 17 00:00:00 2001 From: emiyl Date: Sat, 25 Jul 2026 22:51:27 +0100 Subject: [PATCH 12/12] findElement uses vertextype as well --- src/vm_builtins.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 6fa8e010e..e9b1ab624 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -16483,11 +16483,11 @@ VertexBuffer *getVertexBuffer(int id) { return buffer; } -static VertexElement *findElement(VertexFormat *format, VertexUsage usage) { +static VertexElement *findElement(VertexFormat *format, VertexUsage usage, VertexType type) { for (int i = 0; i < format->numElements; i++) { - if (format->elements[i].usage == usage) { - return &format->elements[i]; - } + VertexElement *e = &format->elements[i]; + if (e->usage == usage && e->type == type) + return e; } return nullptr; } @@ -16843,8 +16843,8 @@ static RValue builtin_vertex_begin(VMContext* ctx, RValue* args, int32_t argCoun return RValue_makeReal(0); } -static bool vertexWrite(VertexBuffer *vb, VertexUsage usage, const void *data, size_t size) { - VertexElement *element = findElement(vb->format, usage); +static bool vertexWrite(VertexBuffer *vb, VertexUsage usage, VertexType type, const void *data, size_t size) { + VertexElement *element = findElement(vb->format, usage, type); if (!element) return false; @@ -16909,7 +16909,7 @@ static RValue builtin_vertex_colour_common( uint32_t packed = convertGMColour(colour, alpha); - vertexWrite(vb, usage, &packed, sizeof(packed)); + vertexWrite(vb, usage, VERTEX_TYPE_COLOR, &packed, sizeof(packed)); return RValue_makeReal(0); } @@ -16939,7 +16939,7 @@ static RValue builtin_vertex_normal(VMContext* ctx, RValue* args, int32_t argCou RValue_toReal(args[3]) }; - vertexWrite(vb, VERTEX_USAGE_NORMAL, data, sizeof(data)); + vertexWrite(vb, VERTEX_USAGE_NORMAL, VERTEX_TYPE_FLOAT3, data, sizeof(data)); return RValue_makeReal(0); } @@ -16960,7 +16960,7 @@ static RValue builtin_vertex_position(VMContext* ctx, RValue* args, int32_t argC RValue_toReal(args[2]) }; - vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(data)); + vertexWrite(vb, VERTEX_USAGE_POSITION, VERTEX_TYPE_FLOAT2, data, sizeof(data)); return RValue_makeReal(0); } @@ -16982,7 +16982,7 @@ static RValue builtin_vertex_position_3d(VMContext* ctx, RValue* args, int32_t a RValue_toReal(args[3]) }; - vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(data)); + vertexWrite(vb, VERTEX_USAGE_POSITION, VERTEX_TYPE_FLOAT3, data, sizeof(data)); return RValue_makeReal(0); } @@ -17001,7 +17001,7 @@ static RValue builtin_vertex_texcoord(VMContext* ctx, RValue* args, int32_t argC RValue_toReal(args[2]) }; - vertexWrite(vb, VERTEX_USAGE_TEXCOORD, data, sizeof(data)); + vertexWrite(vb, VERTEX_USAGE_TEXCOORD, VERTEX_TYPE_FLOAT2, data, sizeof(data)); return RValue_makeReal(0); } @@ -17024,7 +17024,16 @@ static RValue builtin_vertex_floatN( for (int i = 0; i < count; i++) data[i] = RValue_toReal(args[i + 1]); - vertexWrite(vb, VERTEX_USAGE_POSITION, data, sizeof(float) * count); + VertexType type; + switch (count) { + case 1: type = VERTEX_TYPE_FLOAT1; break; + case 2: type = VERTEX_TYPE_FLOAT2; break; + case 3: type = VERTEX_TYPE_FLOAT3; break; + case 4: type = VERTEX_TYPE_FLOAT4; break; + default: return RValue_makeReal(0); + } + + vertexWrite(vb, VERTEX_USAGE_POSITION, type, data, sizeof(float) * count); return RValue_makeReal(0); }static RValue builtin_vertex_float1(VMContext* c, RValue* a, int32_t n) @@ -17062,7 +17071,19 @@ static RValue builtin_vertex_ubyte4(VMContext* ctx, RValue* args, int32_t argCou uint8_t z = (uint8_t)RValue_toInt32(args[3]);; uint8_t w = (uint8_t)RValue_toInt32(args[4]);; - VertexElement *e = findElement(vb->format, VERTEX_USAGE_POSITION); + VertexElement *e = findElement(vb->format, VERTEX_USAGE_COLOR, VERTEX_TYPE_UBYTE4); + if (!e) { + printf("Couldn't find UBYTE4 element!\n"); + + for (int i = 0; i < vb->format->numElements; i++) { + VertexElement *el = &vb->format->elements[i]; + printf("element %d: usage=%d type=%d offset=%d\n", + i, el->usage, el->type, el->offset); + } + + return RValue_makeReal(0); + } + memcpy(vb->currentVertex + e->offset, &x, sizeof(uint8_t)); memcpy(vb->currentVertex + e->offset + sizeof(uint8_t), &y, sizeof(uint8_t)); memcpy(vb->currentVertex + e->offset + sizeof(uint8_t) * 2, &z, sizeof(uint8_t));