From 36f6ac65f2c98311c5dd730b8484e7f4dd02d118 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 2 Jun 2026 09:53:56 -0500 Subject: [PATCH] Builds and works on Gamecube --- assets/testentity.js | 10 +- scripts/build-gamecube-docker.sh | 2 +- scripts/build-gamecube-iso-docker.sh | 2 +- scripts/build-knulli-docker.sh | 2 +- scripts/build-linux-docker.sh | 2 +- scripts/build-psp-docker.sh | 2 +- scripts/build-vita-docker.sh | 2 +- scripts/build-wii-docker.sh | 2 +- scripts/build-wii-iso-docker.sh | 2 +- scripts/test-linux-docker.sh | 2 +- .../component/renderable/modulerenderable.h | 154 ++++++++++++------ types/entity/component/renderable.d.ts | 32 ++-- 12 files changed, 138 insertions(+), 76 deletions(-) diff --git a/assets/testentity.js b/assets/testentity.js index 5a1ef6ea..399d380a 100644 --- a/assets/testentity.js +++ b/assets/testentity.js @@ -17,10 +17,8 @@ const testPos = testEntity.add(Component.POSITION); /** @type {RenderableSpritebatch} */ const testRenderable = testEntity.add(Component.RENDERABLE); -testRenderable.type = Renderable.SPRITEBATCH; -testRenderable.setTexture(entry.texture); -testRenderable.addSprite( - 0, 0, 1, 1, - 0, 0, 1, 1 -); +testRenderable.texture = entry.texture; +testRenderable.sprites = [ + [0, 0, 1, 1, 0, 1, 1, 0] +]; testPos.localPosition = new Vec3(0, 0, 0); diff --git a/scripts/build-gamecube-docker.sh b/scripts/build-gamecube-docker.sh index 8da0b9bb..69d2777b 100755 --- a/scripts/build-gamecube-docker.sh +++ b/scripts/build-gamecube-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-dolphin -f docker/dolphin/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-dolphin /bin/bash -c "./scripts/build-gamecube.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-gamecube.sh" \ No newline at end of file diff --git a/scripts/build-gamecube-iso-docker.sh b/scripts/build-gamecube-iso-docker.sh index b5340f76..24975feb 100755 --- a/scripts/build-gamecube-iso-docker.sh +++ b/scripts/build-gamecube-iso-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-dolphin -f docker/dolphin/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-dolphin /bin/bash -c "./scripts/build-gamecube-iso.sh" +docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-gamecube-iso.sh" diff --git a/scripts/build-knulli-docker.sh b/scripts/build-knulli-docker.sh index b225e4a2..26f8bfc6 100755 --- a/scripts/build-knulli-docker.sh +++ b/scripts/build-knulli-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-knulli -f docker/knulli/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-knulli /bin/bash -c "./scripts/build-knulli.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-knulli /bin/bash -c "./scripts/build-knulli.sh" \ No newline at end of file diff --git a/scripts/build-linux-docker.sh b/scripts/build-linux-docker.sh index a9fc6b0e..b46fddd9 100755 --- a/scripts/build-linux-docker.sh +++ b/scripts/build-linux-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-linux -f docker/linux/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-linux /bin/bash -c "./scripts/build-linux.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-linux /bin/bash -c "./scripts/build-linux.sh" \ No newline at end of file diff --git a/scripts/build-psp-docker.sh b/scripts/build-psp-docker.sh index ecd103e8..06cfd456 100755 --- a/scripts/build-psp-docker.sh +++ b/scripts/build-psp-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-psp -f docker/psp/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-psp /bin/bash -c "./scripts/build-psp.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-psp /bin/bash -c "./scripts/build-psp.sh" \ No newline at end of file diff --git a/scripts/build-vita-docker.sh b/scripts/build-vita-docker.sh index e7719668..a0308cb4 100755 --- a/scripts/build-vita-docker.sh +++ b/scripts/build-vita-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-vita -f docker/vita/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-vita /bin/bash -c "./scripts/build-vita.sh" +docker run --rm -v "$(pwd):/workdir" dusk-vita /bin/bash -c "./scripts/build-vita.sh" diff --git a/scripts/build-wii-docker.sh b/scripts/build-wii-docker.sh index f2893b4c..087f551a 100755 --- a/scripts/build-wii-docker.sh +++ b/scripts/build-wii-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-dolphin -f docker/dolphin/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-dolphin /bin/bash -c "./scripts/build-wii.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-wii.sh" \ No newline at end of file diff --git a/scripts/build-wii-iso-docker.sh b/scripts/build-wii-iso-docker.sh index 043a4bb1..cd05520a 100755 --- a/scripts/build-wii-iso-docker.sh +++ b/scripts/build-wii-iso-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-dolphin -f docker/dolphin/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-dolphin /bin/bash -c "./scripts/build-wii-iso.sh" +docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-wii-iso.sh" diff --git a/scripts/test-linux-docker.sh b/scripts/test-linux-docker.sh index b43a89e6..fb8c9bfc 100755 --- a/scripts/test-linux-docker.sh +++ b/scripts/test-linux-docker.sh @@ -1,3 +1,3 @@ #!/bin/bash docker build -t dusk-linux -f docker/linux/Dockerfile . -docker run --rm -v $(pwd):/workdir dusk-linux /bin/bash -c "./scripts/test-linux.sh" \ No newline at end of file +docker run --rm -v "$(pwd):/workdir" dusk-linux /bin/bash -c "./scripts/test-linux.sh" \ No newline at end of file diff --git a/src/dusk/script/module/entity/component/renderable/modulerenderable.h b/src/dusk/script/module/entity/component/renderable/modulerenderable.h index 0e132117..51a8723c 100644 --- a/src/dusk/script/module/entity/component/renderable/modulerenderable.h +++ b/src/dusk/script/module/entity/component/renderable/modulerenderable.h @@ -34,6 +34,19 @@ static inline entityrenderable_t *moduleRenderableData(const jscomponent_t *c) { ); } +/** Read a float from a JS array at index, returning def if out of range. */ +static inline float_t moduleRenderableArrayFloat( + const jerry_value_t arr, + const uint32_t idx, + const float_t def +) { + if(idx >= jerry_array_length(arr)) return def; + jerry_value_t v = jerry_object_get_index(arr, idx); + float_t f = jerry_value_is_number(v) ? (float_t)jerry_value_as_number(v) : def; + jerry_value_free(v); + return f; +} + moduleBaseFunction(moduleRenderableGetEntity) { jscomponent_t *c = moduleRenderableSelf(callInfo); if(!c) return jerry_undefined(); @@ -104,7 +117,22 @@ moduleBaseFunction(moduleRenderableSetColor) { return jerry_undefined(); } -/* setTexture(tex: Texture) — switches to SPRITEBATCH and stores the texture. */ +/* + * texture getter — returns the pinned Texture instance, or undefined if none. + */ +moduleBaseFunction(moduleRenderableGetTexture) { + jscomponent_t *c = moduleRenderableSelf(callInfo); + if(!c) return jerry_undefined(); + jerry_value_t key = jerry_string_sz("_tex"); + jerry_value_t val = jerry_object_get(callInfo->this_value, key); + jerry_value_free(key); + return val; +} + +/* + * texture setter — switches to SPRITEBATCH, binds the texture, and pins the + * Texture JS object so GC won't free the asset while the pointer is live. + */ moduleBaseFunction(moduleRenderableSetTexture) { moduleBaseRequireArgs(1); jscomponent_t *c = moduleRenderableSelf(callInfo); @@ -115,11 +143,10 @@ moduleBaseFunction(moduleRenderableSetTexture) { &MODULE_TEXTURE_PROTO, args[0] ); if(!tex || !tex->entry) { - return moduleBaseThrow("Renderable.setTexture: expected Texture"); + return moduleBaseThrow("Renderable.texture: expected Texture"); } r->type = ENTITY_RENDERABLE_TYPE_SPRITEBATCH; r->data.spritebatch.texture = &tex->entry->data.texture; - /* Pin the Texture object so GC won't free the asset while we hold a pointer. */ jerry_value_t pinKey = jerry_string_sz("_tex"); jerry_object_set(callInfo->this_value, pinKey, args[0]); jerry_value_free(pinKey); @@ -127,55 +154,89 @@ moduleBaseFunction(moduleRenderableSetTexture) { } /* - * addSprite(x1,y1,z1, x2,y2,z2, u1,v1, u2,v2) - * addSprite(x1,y1, x2,y2, u1,v1, u2,v2) — z defaults to 0 + * sprites getter — returns a JS array of sprite sub-arrays. + * Each element is [x1,y1,z1, x2,y2,z2, u1,v1, u2,v2] (10 numbers). */ -moduleBaseFunction(moduleRenderableAddSprite) { +moduleBaseFunction(moduleRenderableGetSprites) { jscomponent_t *c = moduleRenderableSelf(callInfo); if(!c) return jerry_undefined(); entityrenderable_t *r = moduleRenderableData(c); if(!r) return jerry_undefined(); - entityrenderablespritebatch_t *sb = &r->data.spritebatch; - if(sb->spriteCount >= ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX) { - return moduleBaseThrow("Renderable.addSprite: sprite capacity reached"); + const entityrenderablespritebatch_t *sb = &r->data.spritebatch; + + jerry_value_t arr = jerry_array((uint32_t)sb->spriteCount); + for(uint32_t i = 0; i < (uint32_t)sb->spriteCount; i++) { + const spritebatchsprite_t *s = &sb->sprites[i]; + float_t vals[10] = { + s->min[0], s->min[1], s->min[2], + s->max[0], s->max[1], s->max[2], + s->uvMin[0], s->uvMin[1], + s->uvMax[0], s->uvMax[1], + }; + jerry_value_t sprite = jerry_array(10); + for(uint32_t j = 0; j < 10; j++) { + jerry_value_t num = jerry_number((double)vals[j]); + jerry_object_set_index(sprite, j, num); + jerry_value_free(num); + } + jerry_object_set_index(arr, i, sprite); + jerry_value_free(sprite); } - spritebatchsprite_t s; - if(argc >= 10) { - moduleBaseRequireArgs(10); - s.min[0] = moduleBaseArgFloat(0); - s.min[1] = moduleBaseArgFloat(1); - s.min[2] = moduleBaseArgFloat(2); - s.max[0] = moduleBaseArgFloat(3); - s.max[1] = moduleBaseArgFloat(4); - s.max[2] = moduleBaseArgFloat(5); - s.uvMin[0] = moduleBaseArgFloat(6); - s.uvMin[1] = moduleBaseArgFloat(7); - s.uvMax[0] = moduleBaseArgFloat(8); - s.uvMax[1] = moduleBaseArgFloat(9); - } else { - moduleBaseRequireArgs(8); - s.min[0] = moduleBaseArgFloat(0); - s.min[1] = moduleBaseArgFloat(1); - s.min[2] = 0.0f; - s.max[0] = moduleBaseArgFloat(2); - s.max[1] = moduleBaseArgFloat(3); - s.max[2] = 0.0f; - s.uvMin[0] = moduleBaseArgFloat(4); - s.uvMin[1] = moduleBaseArgFloat(5); - s.uvMax[0] = moduleBaseArgFloat(6); - s.uvMax[1] = moduleBaseArgFloat(7); - } - sb->sprites[sb->spriteCount++] = s; - return jerry_undefined(); + return arr; } -/* clearSprites() — resets the sprite count to 0. */ -moduleBaseFunction(moduleRenderableClearSprites) { +/* + * sprites setter — accepts an array of sub-arrays. + * Each element: 10 numbers (3D) or 8 numbers (2D, z defaults to 0). + */ +moduleBaseFunction(moduleRenderableSetSprites) { + moduleBaseRequireArgs(1); jscomponent_t *c = moduleRenderableSelf(callInfo); if(!c) return jerry_undefined(); entityrenderable_t *r = moduleRenderableData(c); if(!r) return jerry_undefined(); - r->data.spritebatch.spriteCount = 0; + if(!jerry_value_is_array(args[0])) { + return moduleBaseThrow("Renderable.sprites: expected Array"); + } + entityrenderablespritebatch_t *sb = &r->data.spritebatch; + uint32_t count = jerry_array_length(args[0]); + if(count > ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX) { + return moduleBaseThrow("Renderable.sprites: exceeds sprite capacity"); + } + sb->spriteCount = 0; + for(uint32_t i = 0; i < count; i++) { + jerry_value_t elem = jerry_object_get_index(args[0], i); + if(!jerry_value_is_array(elem)) { + jerry_value_free(elem); + return moduleBaseThrow("Renderable.sprites: each element must be an Array"); + } + spritebatchsprite_t s; + if(jerry_array_length(elem) >= 10) { + s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); + s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); + s.min[2] = moduleRenderableArrayFloat(elem, 2, 0.0f); + s.max[0] = moduleRenderableArrayFloat(elem, 3, 0.0f); + s.max[1] = moduleRenderableArrayFloat(elem, 4, 0.0f); + s.max[2] = moduleRenderableArrayFloat(elem, 5, 0.0f); + s.uvMin[0] = moduleRenderableArrayFloat(elem, 6, 0.0f); + s.uvMin[1] = moduleRenderableArrayFloat(elem, 7, 0.0f); + s.uvMax[0] = moduleRenderableArrayFloat(elem, 8, 1.0f); + s.uvMax[1] = moduleRenderableArrayFloat(elem, 9, 1.0f); + } else { + s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); + s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); + s.min[2] = 0.0f; + s.max[0] = moduleRenderableArrayFloat(elem, 2, 0.0f); + s.max[1] = moduleRenderableArrayFloat(elem, 3, 0.0f); + s.max[2] = 0.0f; + s.uvMin[0] = moduleRenderableArrayFloat(elem, 4, 0.0f); + s.uvMin[1] = moduleRenderableArrayFloat(elem, 5, 0.0f); + s.uvMax[0] = moduleRenderableArrayFloat(elem, 6, 1.0f); + s.uvMax[1] = moduleRenderableArrayFloat(elem, 7, 1.0f); + } + jerry_value_free(elem); + sb->sprites[sb->spriteCount++] = s; + } return jerry_undefined(); } @@ -211,14 +272,13 @@ static void moduleRenderableInit(void) { &MODULE_RENDERABLE_PROTO, "color", moduleRenderableGetColor, moduleRenderableSetColor ); - scriptProtoDefineFunc( - &MODULE_RENDERABLE_PROTO, "setTexture", moduleRenderableSetTexture + scriptProtoDefineProp( + &MODULE_RENDERABLE_PROTO, "texture", + moduleRenderableGetTexture, moduleRenderableSetTexture ); - scriptProtoDefineFunc( - &MODULE_RENDERABLE_PROTO, "addSprite", moduleRenderableAddSprite - ); - scriptProtoDefineFunc( - &MODULE_RENDERABLE_PROTO, "clearSprites", moduleRenderableClearSprites + scriptProtoDefineProp( + &MODULE_RENDERABLE_PROTO, "sprites", + moduleRenderableGetSprites, moduleRenderableSetSprites ); scriptProtoDefineToString(&MODULE_RENDERABLE_PROTO, moduleRenderableToString); diff --git a/types/entity/component/renderable.d.ts b/types/entity/component/renderable.d.ts index c20b30b0..d39196cf 100644 --- a/types/entity/component/renderable.d.ts +++ b/types/entity/component/renderable.d.ts @@ -32,27 +32,31 @@ interface RenderableMaterial extends Renderable { /** * Renderable in `SPRITEBATCH` mode. - * Activated by calling `setTexture`. + * + * Set `texture` to activate spritebatch rendering (also switches `type` + * to `Renderable.SPRITEBATCH` automatically). */ interface RenderableSpritebatch extends Renderable { /** - * Assigns a texture and switches to `SPRITEBATCH` mode. - * The Texture object is pinned (GC-safe) for the component's lifetime. + * The bound texture. Assigning a `Texture` switches the renderable to + * `SPRITEBATCH` mode and pins the object against GC. Reading returns the + * same `Texture` instance that was assigned, or `undefined` if none. */ - setTexture(texture: Texture): void; + texture: Texture | undefined; + /** - * Adds a sprite quad to the spritebatch. + * Sprite list. Reading returns a JS array of 10-element sub-arrays + * `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` — one per sprite. * - * 3D form (10 args): `addSprite(x1,y1,z1, x2,y2,z2, u1,v1, u2,v2)` - * 2D form (8 args): `addSprite(x1,y1, x2,y2, u1,v1, u2,v2)` — z defaults to 0 + * Assigning an array replaces all sprites. Each element may be: + * - 10 numbers (3D): `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` + * - 8 numbers (2D, z defaults to 0): `[x1,y1, x2,y2, u1,v1, u2,v2]` + * + * @example + * r.sprites = [[-0.5, 0, 0.5, 1, 0, 0, 1, 1]]; + * r.sprites = []; // clear */ - addSprite( - x1: number, y1: number, z1OrX2: number, - x2OrY2: number, y2OrZ2?: number, z2?: number, - u1?: number, v1?: number, u2?: number, v2?: number - ): void; - /** Resets the sprite count to zero. */ - clearSprites(): void; + sprites: number[][]; } /**