From dfcdaf3b92d886bac03d2611a4b3a4686ab6c308 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Thu, 13 Oct 2022 23:50:57 +0100 Subject: [PATCH] Rendering is working --- README.md | 2 +- assets/scripts/render.toy | 17 ++ assets/scripts/root.toy | 19 +- assets/sprites/character.png | Bin 0 -> 166 bytes core/engine.c | 5 + core/lib_render.c | 364 +++++++++++++++++++++++++++++++++++ core/lib_render.h | 6 + core/makefile | 2 +- core/render_node.c | 87 +++++++++ core/render_node.h | 34 ++++ 10 files changed, 527 insertions(+), 9 deletions(-) create mode 100644 assets/scripts/render.toy create mode 100644 assets/sprites/character.png create mode 100644 core/lib_render.c create mode 100644 core/lib_render.h create mode 100644 core/render_node.c create mode 100644 core/render_node.h diff --git a/README.md b/README.md index d1bf942..091709b 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,4 @@ Make sure the program can see the `assets` folder (symbolic links can help). ## Dependencies * SDL2 - +* SDL2_image diff --git a/assets/scripts/render.toy b/assets/scripts/render.toy new file mode 100644 index 0000000..9397292 --- /dev/null +++ b/assets/scripts/render.toy @@ -0,0 +1,17 @@ +import render; + +fn onInit(node: opaque) { + node.loadTextureRenderNode("assets/sprites/character.png"); +} + +fn onStep(node: opaque) { + // +} + +fn onFree(node: opaque) { + node.freeTextureRenderNode(); +} + +fn onDraw(node: opaque) { + node.drawRenderNode(50, 50); +} \ No newline at end of file diff --git a/assets/scripts/root.toy b/assets/scripts/root.toy index f83756d..4a274ba 100644 --- a/assets/scripts/root.toy +++ b/assets/scripts/root.toy @@ -1,5 +1,6 @@ import standard; import engine; +import render; fn _makeChild(parent: opaque, fname: string, init: bool) { var child: opaque = loadNode(fname); @@ -13,21 +14,25 @@ fn _makeChild(parent: opaque, fname: string, init: bool) { //root node can load the whole scene, and essentially act as the scene object fn onInit(node: opaque) { - print "root.toy:onInit() called"; + //print "root.toy:onInit() called"; //make a child - node.makeChild("assets/scripts/child.toy", true); //indicate whether to call "init" on the new node or not - node.makeChild("assets/scripts/child.toy", false); - node.makeChild("assets/scripts/child.toy", false); + //node.makeChild("assets/scripts/child.toy", true); //indicate whether to call "init" on the new node or not + //node.makeChild("assets/scripts/child.toy", false); + //node.makeChild("assets/scripts/child.toy", false); //actually, grab that first node and free it - node.freeChildNode(0); //must be done from the parent node, so it's pointer can be nullified + //node.freeChildNode(0); //must be done from the parent node, so it's pointer can be nullified + + var r = loadRenderNode("assets/scripts/render.toy"); + + node.pushNode(r); } fn onStep(node: opaque) { - print clock(); + //print clock(); } fn onFree(node: opaque) { - print "root.toy:onFree() called"; + //print "root.toy:onFree() called"; } diff --git a/assets/sprites/character.png b/assets/sprites/character.png new file mode 100644 index 0000000000000000000000000000000000000000..54077361aa50afefe2ac8811386899ea1d353a19 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^5@TQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XB4ude`@%$AjKtXFy7sn6}@3&_bGBPOe968V)IHNuFra*Il y2CGu~Bcp3KUmC=pmN9fcBUpR{nfjeOCFbjN#`>OD9nC;v7(8A5T-G@yGywn(@H5T; literal 0 HcmV?d00001 diff --git a/core/engine.c b/core/engine.c index 3293720..69ef17b 100644 --- a/core/engine.c +++ b/core/engine.c @@ -1,6 +1,7 @@ #include "engine.h" #include "lib_engine.h" +#include "lib_render.h" #include "lib_standard.h" #include "repl_tools.h" @@ -41,6 +42,7 @@ void initEngine() { //init Toy initInterpreter(&engine.interpreter); injectNativeHook(&engine.interpreter, "engine", hookEngine); + injectNativeHook(&engine.interpreter, "render", hookRender); injectNativeHook(&engine.interpreter, "standard", hookStandard); size_t size = 0; @@ -146,6 +148,9 @@ void execEngine() { //render the world SDL_SetRenderDrawColor(engine.renderer, 0, 0, 0, 255); //NOTE: This line can be disabled later SDL_RenderClear(engine.renderer); //NOTE: This line can be disabled later + + callEngineNode(engine.rootNode, &engine.interpreter, "onDraw"); + SDL_RenderPresent(engine.renderer); } } diff --git a/core/lib_render.c b/core/lib_render.c new file mode 100644 index 0000000..a9539e9 --- /dev/null +++ b/core/lib_render.c @@ -0,0 +1,364 @@ +#include "lib_render.h" +#include "render_node.h" + +#include "engine.h" +#include "repl_tools.h" + +#include "memory.h" +#include "literal_array.h" + +static int nativeLoadRenderNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments passed to loadRenderNode\n"); + return -1; + } + + //extract the arguments + Literal fname = popLiteralArray(arguments); + + Literal fnameIdn = fname; + if (IS_IDENTIFIER(fname) && parseIdentifierToValue(interpreter, &fname)) { + freeLiteral(fnameIdn); + } + + //check argument types + if (!IS_STRING(fname)) { + interpreter->errorOutput("Incorrect argument type passed to loadRenderNode\n"); + freeLiteral(fname); + return -1; + } + + //load the new node + size_t size = 0; + char* source = readFile(AS_STRING(fname), &size); + unsigned char* tb = compileString(source, &size); + free((void*)source); + + RenderNode* node = ALLOCATE(RenderNode, 1); + + //BUGFIX: make an inner-interpreter + Interpreter inner; + + //init the inner interpreter manually + initLiteralArray(&inner.literalCache); + inner.scope = pushScope(NULL); + inner.bytecode = tb; + inner.length = size; + inner.count = 0; + inner.codeStart = -1; + inner.depth = interpreter->depth + 1; + inner.panic = false; + initLiteralArray(&inner.stack); + inner.exports = interpreter->exports; + inner.exportTypes = interpreter->exportTypes; + inner.hooks = interpreter->hooks; + setInterpreterPrint(&inner, interpreter->printOutput); + setInterpreterAssert(&inner, interpreter->assertOutput); + setInterpreterError(&inner, interpreter->errorOutput); + + initRenderNode(node, &inner, tb, size); + + //NOTE: initNode() must be called manually + + // return the node + Literal nodeLiteral = TO_OPAQUE_LITERAL(node, -1); + pushLiteralArray(&interpreter->stack, nodeLiteral); + + //cleanup + freeLiteralArray(&inner.stack); + freeLiteralArray(&inner.literalCache); + freeLiteral(fname); + freeLiteral(nodeLiteral); + + return 1; +} + +static int nativeLoadTextureRenderNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments passed to loadTextureRenderNode\n"); + return -1; + } + + //extract the arguments + Literal fname = popLiteralArray(arguments); + Literal nodeLiteral = popLiteralArray(arguments); + + Literal fnameIdn = fname; + if (IS_IDENTIFIER(fname) && parseIdentifierToValue(interpreter, &fname)) { + freeLiteral(fnameIdn); + } + + Literal nodeIdn = nodeLiteral; + if (IS_IDENTIFIER(nodeLiteral) && parseIdentifierToValue(interpreter, &nodeLiteral)) { + freeLiteral(nodeIdn); + } + + //check argument types + if (!IS_STRING(fname) || !IS_OPAQUE(nodeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to loadTextureRenderNode\n"); + freeLiteral(fname); + freeLiteral(nodeLiteral); + return -1; + } + + //actually load TODO: number the opaques, and check the numbers + RenderNode* node = (RenderNode*)AS_OPAQUE(nodeLiteral); + + if (node->texture != NULL) { + freeTextureRenderNode(node); + } + + if (loadTextureRenderNode(node, AS_STRING(fname)) != 0) { + interpreter->errorOutput("Failed to load the texture into the RenderNode\n"); + freeLiteral(fname); + freeLiteral(nodeLiteral); + return -1; + } + + //cleanup + freeLiteral(fname); + freeLiteral(nodeLiteral); + return 0; +} + +static int nativeFreeTextureRenderNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments passed to freeTextureRenderNode\n"); + return -1; + } + + //extract the arguments + Literal nodeLiteral = popLiteralArray(arguments); + + Literal nodeIdn = nodeLiteral; + if (IS_IDENTIFIER(nodeLiteral) && parseIdentifierToValue(interpreter, &nodeLiteral)) { + freeLiteral(nodeIdn); + } + + //check argument types + if (!IS_OPAQUE(nodeLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to freeTextureRenderNode\n"); + freeLiteral(nodeLiteral); + return -1; + } + + //actually load TODO: number the opaques, and check the numbers + RenderNode* node = (RenderNode*)AS_OPAQUE(nodeLiteral); + + if (node->texture != NULL) { + freeTextureRenderNode(node); + } + + //cleanup + freeLiteral(nodeLiteral); + return 0; +} + +static int nativeSetRectRenderNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 5) { + interpreter->errorOutput("Incorrect number of arguments passed to setRectRenderNode\n"); + return -1; + } + + //extract the arguments + Literal h = popLiteralArray(arguments); + Literal w = popLiteralArray(arguments); + Literal y = popLiteralArray(arguments); + Literal x = popLiteralArray(arguments); + Literal nodeLiteral = popLiteralArray(arguments); + + Literal nodeIdn = nodeLiteral; + if (IS_IDENTIFIER(nodeLiteral) && parseIdentifierToValue(interpreter, &nodeLiteral)) { + freeLiteral(nodeIdn); + } + + Literal xi = x; + if (IS_IDENTIFIER(x) && parseIdentifierToValue(interpreter, &x)) { + freeLiteral(xi); + } + + Literal yi = y; + if (IS_IDENTIFIER(y) && parseIdentifierToValue(interpreter, &y)) { + freeLiteral(yi); + } + + Literal wi = w; + if (IS_IDENTIFIER(w) && parseIdentifierToValue(interpreter, &w)) { + freeLiteral(wi); + } + + Literal hi = h; + if (IS_IDENTIFIER(h) && parseIdentifierToValue(interpreter, &h)) { + freeLiteral(hi); + } + + //check argument types + if (!IS_OPAQUE(nodeLiteral) || !IS_INTEGER(x) || !IS_INTEGER(y) || !IS_INTEGER(w) || !IS_INTEGER(h)) { + interpreter->errorOutput("Incorrect argument type passed to setRectRenderNode\n"); + freeLiteral(nodeLiteral); + freeLiteral(xi); + freeLiteral(yi); + freeLiteral(wi); + freeLiteral(hi); + return -1; + } + + //actually set + RenderNode* node = (RenderNode*)AS_OPAQUE(nodeLiteral); + + SDL_Rect r = {AS_INTEGER(x), AS_INTEGER(y), AS_INTEGER(w), AS_INTEGER(h)}; + setRectRenderNode(node, r); + + //cleanup + freeLiteral(nodeLiteral); + freeLiteral(xi); + freeLiteral(yi); + freeLiteral(wi); + freeLiteral(hi); + return 0; +} + +static int nativeDrawRenderNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 3 && arguments->count != 5) { + interpreter->errorOutput("Incorrect number of arguments passed to drawRenderNode\n"); + return -1; + } + + //extract the arguments + Literal w = TO_NULL_LITERAL, h = TO_NULL_LITERAL; + if (arguments->count == 5) { + h = popLiteralArray(arguments); + w = popLiteralArray(arguments); + } + + Literal y = popLiteralArray(arguments); + Literal x = popLiteralArray(arguments); + Literal nodeLiteral = popLiteralArray(arguments); + + Literal nodeIdn = nodeLiteral; + if (IS_IDENTIFIER(nodeLiteral) && parseIdentifierToValue(interpreter, &nodeLiteral)) { + freeLiteral(nodeIdn); + } + + Literal xi = x; + if (IS_IDENTIFIER(x) && parseIdentifierToValue(interpreter, &x)) { + freeLiteral(xi); + } + + Literal yi = y; + if (IS_IDENTIFIER(y) && parseIdentifierToValue(interpreter, &y)) { + freeLiteral(yi); + } + + Literal wi = w; + if (IS_IDENTIFIER(w) && parseIdentifierToValue(interpreter, &w)) { + freeLiteral(wi); + } + + Literal hi = h; + if (IS_IDENTIFIER(h) && parseIdentifierToValue(interpreter, &h)) { + freeLiteral(hi); + } + + //check argument types + if (!IS_OPAQUE(nodeLiteral) || !IS_INTEGER(x) || !IS_INTEGER(y) || (!IS_INTEGER(w) && !IS_NULL(w)) || (!IS_INTEGER(h) && !IS_NULL(h))) { + interpreter->errorOutput("Incorrect argument type passed to drawRenderNode\n"); + freeLiteral(nodeLiteral); + freeLiteral(xi); + freeLiteral(yi); + freeLiteral(wi); + freeLiteral(hi); + return -1; + } + + //actually render + RenderNode* node = (RenderNode*)AS_OPAQUE(nodeLiteral); + + SDL_Rect r = {AS_INTEGER(x), AS_INTEGER(y), 0, 0}; + if (IS_INTEGER(w) && IS_INTEGER(h)) { + r.w = AS_INTEGER(w); + r.h = AS_INTEGER(h); + } + else { + r.w = node->rect.w; + r.h = node->rect.h; + } + + drawRenderNode(node, r); + + //cleanup + freeLiteral(nodeLiteral); + freeLiteral(xi); + freeLiteral(yi); + freeLiteral(wi); + freeLiteral(hi); + return 0; +} + +//call the hook +typedef struct Natives { + char* name; + NativeFn fn; +} Natives; + +int hookRender(Interpreter* interpreter, Literal identifier, Literal alias) { + //build the natives list + Natives natives[] = { + {"loadRenderNode", nativeLoadRenderNode}, + {"_loadTextureRenderNode", nativeLoadTextureRenderNode}, + {"_freeTextureRenderNode", nativeFreeTextureRenderNode}, + {"_setRectRenderNode", nativeSetRectRenderNode}, + {"_drawRenderNode", nativeDrawRenderNode}, + {NULL, NULL} + }; + + //store the library in an aliased dictionary + if (!IS_NULL(alias)) { + //make sure the name isn't taken + if (isDelcaredScopeVariable(interpreter->scope, alias)) { + interpreter->errorOutput("Can't override an existing variable\n"); + freeLiteral(alias); + return false; + } + + //create the dictionary to load up with functions + LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1); + initLiteralDictionary(dictionary); + + //load the dict with functions + for (int i = 0; natives[i].name; i++) { + Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name)); + Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0); + func.type = LITERAL_FUNCTION_NATIVE; + + setLiteralDictionary(dictionary, name, func); + + freeLiteral(name); + freeLiteral(func); + } + + //build the type + Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true); + Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true); + Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true); + TYPE_PUSH_SUBTYPE(&type, strType); + TYPE_PUSH_SUBTYPE(&type, fnType); + + //set scope + Literal dict = TO_DICTIONARY_LITERAL(dictionary); + declareScopeVariable(interpreter->scope, alias, type); + setScopeVariable(interpreter->scope, alias, dict, false); + + //cleanup + freeLiteral(dict); + freeLiteral(type); + return 0; + } + + //default + for (int i = 0; natives[i].name; i++) { + injectNativeFn(interpreter, natives[i].name, natives[i].fn); + } + + return 0; +} diff --git a/core/lib_render.h b/core/lib_render.h new file mode 100644 index 0000000..a8d9a3e --- /dev/null +++ b/core/lib_render.h @@ -0,0 +1,6 @@ +#pragma once + +#include "interpreter.h" + +int hookRender(Interpreter* interpreter, Literal identifier, Literal alias); + diff --git a/core/makefile b/core/makefile index a387513..070de35 100644 --- a/core/makefile +++ b/core/makefile @@ -2,7 +2,7 @@ CC=gcc IDIR+=. ../Toy/source CFLAGS+=$(addprefix -I,$(IDIR)) -DSDL_MAIN_HANDLED -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -LIBS+=-lSDL2 -ltoy +LIBS+=-lSDL2 -lSDL2_image -ltoy ODIR = obj SRC = $(wildcard *.c) diff --git a/core/render_node.c b/core/render_node.c new file mode 100644 index 0000000..f33d0d4 --- /dev/null +++ b/core/render_node.c @@ -0,0 +1,87 @@ +#include "render_node.h" + +#include "engine.h" +#include "memory.h" + +#include + +static void freeMemory(void* ptr) { + RenderNode* node = (RenderNode*)ptr; + + SDL_DestroyTexture(node->texture); + + //free this node type's memory + FREE(RenderNode, ptr); +} + +//duplicate the initEngineNode() functionality, plus extra stuff +void initRenderNode(RenderNode* node, Interpreter* interpreter, void* tb, size_t size) { + //init + node->freeMemory = freeMemory; + node->functions = ALLOCATE(LiteralDictionary, 1); + node->children = NULL; + node->capacity = 0; + node->count = 0; + + node->texture = NULL; + + initLiteralDictionary(node->functions); + + //run bytecode + runInterpreter(interpreter, tb, size); + + //grab all top-level function literals + LiteralDictionary* variablesPtr = &interpreter->scope->variables; + + for (int i = 0; i < variablesPtr->capacity; i++) { + //skip empties and tombstones + if (IS_NULL(variablesPtr->entries[i].key)) { + continue; + } + + //if this variable is a function + _entry* entry = &variablesPtr->entries[i]; + if (IS_FUNCTION(entry->value)) { + //save a copy + setLiteralDictionary(node->functions, entry->key, entry->value); + } + } +} + +int loadTextureRenderNode(RenderNode* node, char* fname) { + SDL_Surface* surface = IMG_Load(fname); + + if (surface == NULL) { + return -1; + } + + node->texture = SDL_CreateTextureFromSurface(engine.renderer, surface); + + if (node->texture == NULL) { + return -2; + } + + SDL_FreeSurface(surface); + + int w, h; + SDL_QueryTexture(node->texture, NULL, NULL, &w, &h); + SDL_Rect r = { 0, 0, w, h }; + setRectRenderNode(node, r); + + return 0; +} + +void freeTextureRenderNode(RenderNode* node) { + if (node->texture != NULL) { + SDL_DestroyTexture(node->texture); + node->texture = NULL; + } +} + +void setRectRenderNode(RenderNode* node, SDL_Rect rect) { + node->rect = rect; +} + +void drawRenderNode(RenderNode* node, SDL_Rect dest) { + SDL_RenderCopy(engine.renderer, node->texture, &node->rect, &dest); +} diff --git a/core/render_node.h b/core/render_node.h new file mode 100644 index 0000000..240996e --- /dev/null +++ b/core/render_node.h @@ -0,0 +1,34 @@ +#pragma once + +#include "common.h" + +//mimic the engine node +#include "engine_node.h" + +#include + +typedef struct _renderNode { + //function for releasing memory + EngineNodeCallback freeMemory; + + //toy functions, stored in a dict for flexibility + LiteralDictionary* functions; + + //use Toy's memory model + EngineNode** children; + int capacity; + int count; //includes tombstones + + //RenderNode-specific features + SDL_Texture* texture; + SDL_Rect rect; + //TODO: depth +} RenderNode; + +CORE_API void initRenderNode(RenderNode* node, Interpreter* interpreter, void* tb, size_t size); +CORE_API int loadTextureRenderNode(RenderNode* node, char* fname); +CORE_API void freeTextureRenderNode(RenderNode* node); + +CORE_API void setRectRenderNode(RenderNode* node, SDL_Rect rect); +//TODO: getRectRenderNode +CORE_API void drawRenderNode(RenderNode* node, SDL_Rect dest);