diff --git a/.gitignore b/.gitignore index 5ea5f5e..8e8835a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ nimcache testresults *.exe +.modules diff --git a/config.nims b/config.nims index a7cc64f..efe9bea 100644 --- a/config.nims +++ b/config.nims @@ -1,4 +1,18 @@ import os +import strformat + +# NOTE: Must copy or symlink dependencies into ./.modules +const deps = [ + "safeseq", + "nimtest", + "sdl2_nim", + "seq2d", + "zippy" +] + +for dep in deps: + switch("path", fmt"./.modules/{dep}") + switch("path", fmt"./.modules/{dep}/src") switch("gc", "orc") switch("multimethods", "on") @@ -26,3 +40,74 @@ when defined(linux): putEnv("LD_LIBRARY_PATH", libPath) putEnv("LIBRARY_PATH", libPath) +task create_deps_artifact, "Compresses contents of .usr dir needed for development": + exec "nim r -d:release src/shade.nim --compress" + +task fetch_deps, "Fetches dependencies and extracts them to .usr/lib": + exec "nim r -d:release -d:ssl src/shade.nim --fetch" + +task extract_deps, "Extracts local dependencies (deps_artifact.tar.gz) to .usr/lib": + exec "nim r -d:release -d:ssl src/shade.nim --extract" + +# Tasks +task build_deps, "Builds submodule dependencies": + exec "git submodule update --init" + when defined(linux): + let localUsrPath = joinPath(thisDir(), ".usr") + withDir "submodules/sdl": + exec fmt"./configure --prefix={localUsrPath} --enable-hidapi-libusb" + exec "make -j install" + + withDir "submodules/sdl-gpu": + mkDir "build" + withDir "build": + exec fmt"cmake -B . -S .. -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX={localUsrPath}" + exec "make -j install" + + withDir "submodules/sdl_ttf": + exec fmt"./configure --prefix={localUsrPath}" + exec "make -j install" + + withDir "submodules/sdl_mixer": + mkDir "build" + withDir "build": + exec fmt"../configure --prefix={localUsrPath}" + exec "make -j" + exec "make install" + + withDir fmt"{localUsrPath}/lib": + exec "rm -r *.a *.la cmake pkgconfig" + +task shaders, "Runs the shader example": + exec "nim r -d:release examples/shaders/water_shader.nim" + +task post_shader, "Runs the post-processing shader example": + exec "nim r -d:release examples/shaders/postprocessing.nim" + +task animations, "Runs the animation player example": + exec "nim r examples/basic/animationplayer_example.nim" + +task physics, "Runs the physics example": + exec "nim r -d:release examples/physics/physics_example.nim" + +task physicsd, "Runs the physics example in debug mode": + exec "nim r -d:debug -d:collisionoutlines -d:spatialgrid examples/physics/physics_example.nim" + +task platformer, "Runs the platformer example": + exec "nim r -d:release examples/platformer/platformer_example.nim" + +task platformerd, "Runs the platformer example in debug mode": + exec "nim r -d:debug -d:collisionoutlines examples/platformer/platformer_example.nim" + +task particles, "Runs the particles example": + exec "nim r -d:release examples/particles/particles_example.nim" + +task particlesd, "Runs the particles example in debug mode": + exec "nim r -d:debug examples/particles/particles_example.nim" + +task textbox, "Runs the textbox example": + exec "nim r -d:release examples/textbox/textbox_example.nim" + +task runtests, "Runs all tests": + exec "nimtest" + diff --git a/examples/assets/images/default.png b/examples/assets/images/default.png new file mode 100644 index 0000000..bff6162 Binary files /dev/null and b/examples/assets/images/default.png differ diff --git a/examples/shaders/common.vert b/examples/shaders/common.vert index bd74236..d0b76d5 100644 --- a/examples/shaders/common.vert +++ b/examples/shaders/common.vert @@ -1,15 +1,12 @@ +#version 400 attribute vec3 gpu_Vertex; attribute vec2 gpu_TexCoord; -attribute vec4 gpu_Color; uniform mat4 gpu_ModelViewProjectionMatrix; -varying vec4 color; varying vec2 texCoord; -void main(void) -{ - color = gpu_Color; - texCoord = vec2(gpu_TexCoord); - gl_Position = gpu_ModelViewProjectionMatrix * vec4(gpu_Vertex, 1.0); +void main(void) { + texCoord = gpu_TexCoord; + gl_Position = gpu_ModelViewProjectionMatrix * vec4(gpu_Vertex, 1.0); } diff --git a/examples/shaders/rectangle.frag b/examples/shaders/rectangle.frag index cc00295..3582ecf 100644 --- a/examples/shaders/rectangle.frag +++ b/examples/shaders/rectangle.frag @@ -1,8 +1,7 @@ -#version 330 +#version 400 varying vec2 vertex; varying vec2 texCoord; -varying vec4 color; uniform sampler2D tex; uniform float time; @@ -10,7 +9,6 @@ uniform vec2 resolution; // See the "gl_FragCoord" section of https://thebookofshaders.com/03/ void main(void) { - vec2 st = gl_FragCoord.xy / resolution; - gl_FragColor = vec4(st.x, st.y, 0.0, 1.0); + gl_FragColor = vec4(texCoord, 0.5, 1.0); } diff --git a/examples/shaders/rectangle_shader.nim b/examples/shaders/rectangle_shader.nim index 835e4c5..3699021 100644 --- a/examples/shaders/rectangle_shader.nim +++ b/examples/shaders/rectangle_shader.nim @@ -15,28 +15,31 @@ const fragShaderPath = "./examples/shaders/rectangle.frag" vertShaderPath = "./examples/shaders/common.vert" -let shaderProgram = newShader(vertShaderPath, fragShaderPath) +let (_, image) = Images.loadImage("./examples/assets/images/default.png") +setImageFilter(image, FILTER_NEAREST) -type Background = ref object of Node +let shaderProgram = newShader(vertShaderPath, fragShaderPath) -Background.renderAsEntityChild: - ctx.rectangleFilled( - 0, - 0, - gamestate.resolution.x, - gamestate.resolution.y, - WHITE +type Rectangle = ref object of Entity + image: Image + +Rectangle.renderAsEntityChild: + blitScale( + image, + nil, + ctx, + gamestate.resolution.x / 2, + gamestate.resolution.y / 2, + 16, + 16, ) -let bg = Background(shader: shaderProgram) -initNode(Node bg, RENDER) +let bg = Rectangle(shader: shaderProgram) +initNode(Entity bg, RENDER) layer.addChild(bg) -Input.addKeyPressedListener( - K_ESCAPE, - proc(key: Keycode, state: KeyState) = - Game.stop() -) +Input.onKeyEvent(K_ESCAPE): + Game.stop() Game.start() diff --git a/examples/shaders/water.frag b/examples/shaders/water.frag index 0b386f8..79b6d10 100644 --- a/examples/shaders/water.frag +++ b/examples/shaders/water.frag @@ -6,7 +6,6 @@ varying vec2 vertex; varying vec2 texCoord; -varying vec4 color; uniform sampler2D tex; uniform float time; @@ -106,7 +105,8 @@ void main(void) { } float t = time * 0.5; - vec2 coord = gl_FragCoord.xy * 0.015 - vec2(t * 0.5, resolution.y / 2.0); + ivec2 texSize = textureSize(tex, 0); + vec2 coord = gl_FragCoord.xy * 0.015 - vec2(t * 0.5, texSize.y / 2.0); float speed = 0.3 * SPEED; float limit = 0.1; float border = 0.025; diff --git a/examples/shaders/water_shader.nim b/examples/shaders/water_shader.nim index 82d4b93..0803438 100644 --- a/examples/shaders/water_shader.nim +++ b/examples/shaders/water_shader.nim @@ -15,21 +15,25 @@ const fragShaderPath = "./examples/shaders/water.frag" vertShaderPath = "./examples/shaders/common.vert" -let shaderProgram = newShader(vertShaderPath, fragShaderPath) +let (_, image) = Images.loadImage("./examples/assets/images/default.png") -type Background = ref object of Entity +let shaderProgram = newShader(vertShaderPath, fragShaderPath) -Background.renderAsEntityChild: - ctx.rectangleFilled( - 0, - 0, - gamestate.resolution.x, - gamestate.resolution.y, - WHITE +type Rectangle = ref object of Node + +Rectangle.renderAsNodeChild: + blitScale( + image, + nil, + ctx, + gamestate.resolution.x / 2, + gamestate.resolution.y / 2, + 16, + 16, ) -let bg = Background(shader: shaderProgram) -initEntity(Entity bg, RENDER) +let bg = Rectangle(shader: shaderProgram) +initNode(Node bg, RENDER) layer.addChild(bg) Input.onKeyPressed(K_ESCAPE): diff --git a/shade.nimble b/shade.nimble deleted file mode 100644 index fd86e45..0000000 --- a/shade.nimble +++ /dev/null @@ -1,93 +0,0 @@ -import - std/os, - strformat - -# Package - -version = "0.1.0" -author = "Einheit Technologies" -description = "Game Engine" -license = "GPLv2.0" -installDirs = @[ "src", "examplegame" ] -namedBin["src/shadepkg/buildtool"] = "src/shade" - -# Dependencies - -requires "nim >= 2.0.0" -requires "zippy == 0.9.7" -requires "https://github.com/avahe-kellenberger/sdl2_nim#head" -requires "safeseq >= 0.1.0" -requires "nimtest >= 0.1.2" -requires "seq2d >= 0.1.1" - -task create_deps_artifact, "Compresses contents of .usr dir needed for development": - exec "nim r -d:release src/shade.nim --compress" - -task fetch_deps, "Fetches dependencies and extracts them to .usr/lib": - exec "nim r -d:release -d:ssl src/shade.nim --fetch" - -task extract_deps, "Extracts local dependencies (deps_artifact.tar.gz) to .usr/lib": - exec "nim r -d:release -d:ssl src/shade.nim --extract" - -# Tasks -task build_deps, "Builds submodule dependencies": - exec "git submodule update --init" - when defined(linux): - let localUsrPath = joinPath(thisDir(), ".usr") - withDir "submodules/sdl": - exec fmt"./configure --prefix={localUsrPath} --enable-hidapi-libusb" - exec "make -j install" - - withDir "submodules/sdl-gpu": - mkDir "build" - withDir "build": - exec fmt"cmake -B . -S .. -G 'Unix Makefiles' -DCMAKE_INSTALL_PREFIX={localUsrPath}" - exec "make -j install" - - withDir "submodules/sdl_ttf": - exec fmt"./configure --prefix={localUsrPath}" - exec "make -j install" - - withDir "submodules/sdl_mixer": - mkDir "build" - withDir "build": - exec fmt"../configure --prefix={localUsrPath}" - exec "make -j" - exec "make install" - - withDir fmt"{localUsrPath}/lib": - exec "rm -r *.a *.la cmake pkgconfig" - -task shaders, "Runs the shader example": - exec "nim r -d:release examples/shaders/water_shader.nim" - -task post_shader, "Runs the post-processing shader example": - exec "nim r -d:release examples/shaders/postprocessing.nim" - -task animations, "Runs the animation player example": - exec "nim r examples/basic/animationplayer_example.nim" - -task physics, "Runs the physics example": - exec "nim r -d:release examples/physics/physics_example.nim" - -task physicsd, "Runs the physics example in debug mode": - exec "nim r -d:debug -d:collisionoutlines -d:spatialgrid examples/physics/physics_example.nim" - -task platformer, "Runs the platformer example": - exec "nim r -d:release examples/platformer/platformer_example.nim" - -task platformerd, "Runs the platformer example in debug mode": - exec "nim r -d:debug -d:collisionoutlines examples/platformer/platformer_example.nim" - -task particles, "Runs the particles example": - exec "nim r -d:release examples/particles/particles_example.nim" - -task particlesd, "Runs the particles example in debug mode": - exec "nim r -d:debug examples/particles/particles_example.nim" - -task textbox, "Runs the textbox example": - exec "nim r -d:release examples/textbox/textbox_example.nim" - -task runtests, "Runs all tests": - exec "nimtest" - diff --git a/src/shadepkg/game/animation.nim b/src/shadepkg/game/animation.nim index 157a830..0499137 100644 --- a/src/shadepkg/game/animation.nim +++ b/src/shadepkg/game/animation.nim @@ -179,7 +179,7 @@ macro addNewAnimationTrack*[T: TrackType]( field: T, frames: openArray[Keyframe[T]], wrapInterpolation: bool = false, - ease: EasingFunction[T] = lerp + ease: EasingFunction[T] = mathutils.lerp ) = ## Adds a new "track" to the animation. ## This is a value that's updated at set intervals as the animation is updated. diff --git a/src/shadepkg/game/camera.nim b/src/shadepkg/game/camera.nim index 1c4f75e..ded1a1b 100644 --- a/src/shadepkg/game/camera.nim +++ b/src/shadepkg/game/camera.nim @@ -8,7 +8,7 @@ export entity, aabb, vector2, mathutils type Camera* = ref object of Entity z*: float - bounds: AABB + bounds*: AABB viewport*: AABB # For entity tracking @@ -83,8 +83,9 @@ proc bounds*(this: Camera): AABB = template confineToBounds(this: Camera) = if this.bounds != AABB_ZERO: let - halfViewportWidth = this.viewport.width * 0.5 - halfViewportHeight = this.viewport.height * 0.5 + distToPlane = 1.0 - this.z + halfViewportWidth = this.viewport.width * 0.5 * distToPlane + halfViewportHeight = this.viewport.height * 0.5 * distToPlane this.x = clamp( this.bounds.left + halfViewportWidth, @@ -111,10 +112,6 @@ proc screenToWorldCoord*(this: Camera, screenPoint: Vector, relativeZ: float = 1 template screenToWorldCoord*(this: Camera, x, y: float|int, relativeZ: float = 1.0): Vector = this.screenToWorldCoord(vector(x, y), relativeZ) -method setLocation*(this: Camera, x, y: float) = - procCall Entity(this).setLocation(x, y) - this.updateViewport() - method update*(this: Camera, deltaTime: float) = procCall Entity(this).update(deltaTime) diff --git a/src/shadepkg/game/entity.nim b/src/shadepkg/game/entity.nim index 527a566..2a71d10 100644 --- a/src/shadepkg/game/entity.nim +++ b/src/shadepkg/game/entity.nim @@ -6,7 +6,6 @@ import node, gamestate, ../render/render, - ../render/shader, ../math/vector2 export @@ -17,7 +16,6 @@ export type Entity* = ref object of Node - shader*: Shader location: Vector # Rotation in degrees (clockwise). rotation*: float @@ -63,7 +61,3 @@ method move*(this: Entity, v: Vector) {.base.} = method hash*(this: Entity): Hash {.base.} = return hash(this[].unsafeAddr) -Entity.renderAsChildOf(Node): - if this.shader != nil: - this.shader.render(gamestate.runTime, gamestate.resolution) - diff --git a/src/shadepkg/game/game.nim b/src/shadepkg/game/game.nim index c09caa3..197516d 100644 --- a/src/shadepkg/game/game.nim +++ b/src/shadepkg/game/game.nim @@ -6,6 +6,7 @@ import import refresh_rate_calculator as refresh_rate_calculator_module, scene, + camera, gamestate, ../input/inputhandler, ../audio/audioplayer, @@ -28,6 +29,8 @@ type useFixedDeltaTime*: bool postProcessingShader*: Shader + gameWidth*: int + gameHeight*: int proc detectWindowScaling(this: Engine): Vector proc update*(this: Engine, deltaTime: float) @@ -70,6 +73,9 @@ proc initEngineSingleton*( Game = Engine() + Game.gameWidth = gameWidth + Game.gameHeight = gameHeight + if target.context != nil: Game.window = getWindowFromId(target.context.windowID) Game.window.setWindowTitle(title) @@ -115,9 +121,6 @@ proc initEngineSingleton*( Input.onEvent(FINGERDOWN): Game.ui.handlePress(float e.tfinger.x, float e.tfinger.y) -template screen*(this: Engine): Target = - this.screen - template scene*(this: Engine): Scene = this.scene @@ -158,25 +161,88 @@ proc loop(this: Engine) = refreshRateCalculator: RefreshRateCalculator var - imageNeedsResize = false - image = createImage(this.screen.w, this.screen.h, FORMAT_RGBA) + # NOTE: Adding 2 so we can center the smooth camera rect by 1 pixel, below + image = createImage(uint16 this.gameWidth + 2, uint16 this.gameHeight + 2, FORMAT_RGBA) renderTarget = getTarget(image) - gamestate.onResolutionChanged: - imageNeedsResize = true + # TODO: Need this option for pixel art + image.setImageFilter(FILTER_LINEAR) while not this.shouldExit: - if imageNeedsResize: - freeImage(image) - image = createImage(this.screen.w, this.screen.h, FORMAT_RGBA) - renderTarget = getTarget(image) - this.handleEvents() this.update(deltaTime) this.render(renderTarget) - Input.resetFrameSpecificState() + + let + aspectX = float(this.screen.w) / float(image.w) + aspectY = float(this.screen.h) / float(image.h) + maxAspect = max(aspectX, aspectY) + + if this.scene.camera != nil: + let + offsetX = max(0.001, this.scene.camera.x - floor(this.scene.camera.x)) + offsetY = max(0.001, this.scene.camera.y - floor(this.scene.camera.y)) + # TODO: The second 1.0 should be the plane's z coordinate + # This all should be put into Scene, and have it be responsible for rendering each layer + inversedScalar = 1.0 / (1.0 - this.scene.camera.z) + + # NOTE: The offset ISN'T scaled, we scale it ourselves. + var rect: sdl_gpu.Rect = ( + cfloat(offsetX * inversedScalar - 1.0), + cfloat(offsetY * inversedScalar - 1.0), + cfloat image.w, + cfloat image.h, + ) + + if this.postProcessingShader != nil: + renderWith(this.postProcessingShader): + blitScale( + image, + rect.addr, + this.screen, + float(this.screen.w) / 2.0, + float(this.screen.h) / 2.0, + maxAspect, + maxAspect + ) + else: + blitScale( + image, + rect.addr, + this.screen, + float(this.screen.w) / 2.0, + float(this.screen.h) / 2.0, + maxAspect, + maxAspect + ) + + else: + if this.postProcessingShader != nil: + renderWith(this.postProcessingShader): + blitScale( + image, + nil, + this.screen, + float(this.screen.w) / 2.0, + float(this.screen.h) / 2.0, + maxAspect, + maxAspect + ) + else: + blitScale( + image, + nil, + this.screen, + float(this.screen.w) / 2.0, + float(this.screen.h) / 2.0, + maxAspect, + maxAspect + ) + flip(this.screen) + Input.resetFrameSpecificState() + let time = getMonoTime().ticks let elapsedNanos = time - previousTimeNanos previousTimeNanos = time @@ -238,12 +304,6 @@ proc render*(this: Engine, ctx: Target) = this.ui.layout(gamestate.resolution.x, gamestate.resolution.y) this.ui.render(ctx) - if this.postProcessingShader != nil: - this.postProcessingShader.render(gamestate.runTime, gamestate.resolution) - # Restore normal matrix popMatrix() - # Render the image to the screen - ctx.image.blit(nil, this.screen, gamestate.resolution.x * 0.5, gamestate.resolution.y * 0.5) - diff --git a/src/shadepkg/game/layer.nim b/src/shadepkg/game/layer.nim index beb6392..1f78465 100644 --- a/src/shadepkg/game/layer.nim +++ b/src/shadepkg/game/layer.nim @@ -1,6 +1,7 @@ import node -import ../render/render -import std/algorithm +import ../render/[render, shader] + +import safeseq export node @@ -14,14 +15,17 @@ type ## All nodes on the layer are assumed to share this same coordinate. ## Layer* = ref object of RootObj - children: seq[Node] + children: SafeSeq[Node] # Location of the layer on the `z` axis. z: float zChangeListeners: seq[ZChangeListener] onUpdate*: proc(this: Layer, deltaTime: float) + # TODO: Give shaders to layers? + proc initLayer*(layer: Layer, z: float = 1.0) = layer.z = z + layer.children = newSafeSeq[Node]() proc newLayer*(z: float = 1.0): Layer = result = Layer() @@ -37,8 +41,8 @@ proc `z=`*(this: Layer, z: float) = for listener in this.zChangeListeners: listener(oldZ, this.z) -proc sortChildren*(this: Layer, cmp: proc (x, y: Node): int) = - this.children.sort(cmp) +# proc sortChildren*(this: Layer, cmp: proc (x, y: Node): int) = +# this.children.sort(cmp) method addChild*(this: Layer, child: Node) {.base.} = ## Adds the child to this Layer. @@ -81,12 +85,21 @@ method update*(this: Layer, deltaTime: float) {.base.} = if child.shouldUpdate: update(child, deltaTime) - for i in countdown(this.children.len() - 1, 0): - if this.children[i].isDead: - this.children.del(i) + # for i in countdown(this.children.len() - 1, 0): + # if this.children[i].isDead: + # this.children.del(i) + + if not this.children.areElementsLocked(): + this.children.keepItIf(not it.isDead) Layer.renderAsParent: for child in this: - if child.shouldRender: + if not child.shouldRender: + continue + + if child.shader != nil: + renderWith(child.shader): + child.render(ctx, offsetX, offsetY) + else: child.render(ctx, offsetX, offsetY) diff --git a/src/shadepkg/game/node.nim b/src/shadepkg/game/node.nim index eeab140..177de67 100644 --- a/src/shadepkg/game/node.nim +++ b/src/shadepkg/game/node.nim @@ -1,4 +1,5 @@ -import ../render/render +import ../render/[render, shader] +export shader type ## Flags indicating how the object should be treated. @@ -8,6 +9,7 @@ type flags*: NodeFlags # Invoked after this node has been updated. onUpdate*: proc(this: Node, deltaTime: float) + shader*: Shader const DEAD* = 0b0001'u8 diff --git a/src/shadepkg/game/physicslayer.nim b/src/shadepkg/game/physicslayer.nim index aca070c..f9ca749 100644 --- a/src/shadepkg/game/physicslayer.nim +++ b/src/shadepkg/game/physicslayer.nim @@ -125,6 +125,7 @@ template handleCollision*(this: PhysicsLayer, bodyA, bodyB: PhysicsBody) = if collision == nil: continue + # TODO: Need a way to not "resolve" the collision, but notify of overlap? collision.resolve(bodyA, bodyB) # Register the collision for any existing callbacks. diff --git a/src/shadepkg/game/scene.nim b/src/shadepkg/game/scene.nim index d723073..3d11e88 100644 --- a/src/shadepkg/game/scene.nim +++ b/src/shadepkg/game/scene.nim @@ -3,8 +3,7 @@ import ../render/render import layer, - camera, - gamestate + camera export layer @@ -15,9 +14,9 @@ type Scene* = ref object proc initScene*(scene: Scene) = scene.isLayerOrderValid = true - gamestate.onResolutionChanged: - if scene.camera != nil: - scene.camera.updateViewport() + # gamestate.onResolutionChanged: + # if scene.camera != nil: + # scene.camera.updateViewport() proc newScene*(): Scene = result = Scene() diff --git a/src/shadepkg/input/inputhandler.nim b/src/shadepkg/input/inputhandler.nim index 60f195e..dd67f69 100644 --- a/src/shadepkg/input/inputhandler.nim +++ b/src/shadepkg/input/inputhandler.nim @@ -186,7 +186,9 @@ proc initInputHandlerSingleton*(windowScaling: Vector) = customActions: initTable[string, ptr InputState]() ) - if init(INIT_GAMECONTROLLER) != 0: + # TODO: Put this back in when it stops lagging + # if init(INIT_GAMECONTROLLER) != 0: + if init(INIT_EVENTS) != 0: raise newException(Exception, "Unable to init controller support") proc addListener*(this: InputHandler, eventKind: EventKind, listener: EventListener) = diff --git a/src/shadepkg/math/aabb.nim b/src/shadepkg/math/aabb.nim index 053db7b..aa12327 100644 --- a/src/shadepkg/math/aabb.nim +++ b/src/shadepkg/math/aabb.nim @@ -12,6 +12,9 @@ type AABB* = object template aabb*(left, top, right, bottom: float): AABB = AABB(topLeft: vector(left, top), bottomRight: vector(right, bottom)) +template aabb*(left, top, right, bottom: int): AABB = + AABB(topLeft: vector(left, top), bottomRight: vector(right, bottom)) + const AABB_ZERO* = aabb(0, 0, 0, 0) AABB_INF* = aabb(NegInf, NegInf, Inf, Inf) diff --git a/src/shadepkg/render/render.nim b/src/shadepkg/render/render.nim index c883cc8..b374fc1 100644 --- a/src/shadepkg/render/render.nim +++ b/src/shadepkg/render/render.nim @@ -18,6 +18,15 @@ template renderAsChildOf*(ChildType, ParentType: typedesc, body: untyped): untyp procCall `ParentType`(this).render(ctx, offsetX, offsetY) `body` +template renderAsNodeChild*(ChildType: typedesc, body: untyped): untyped = + ## Helper for the render method. + ## + ## Example: + ## renderAsNodeChild(T): + ## ctx.blit(image, this.x + offsetX, this.y + offsetY) + ChildType.renderAsChildOf(Node): + body + template renderAsEntityChild*(ChildType: typedesc, body: untyped): untyped = ## Helper for the render method. ## diff --git a/src/shadepkg/render/shader.nim b/src/shadepkg/render/shader.nim index 43ac310..c357cce 100644 --- a/src/shadepkg/render/shader.nim +++ b/src/shadepkg/render/shader.nim @@ -1,6 +1,5 @@ import ../game/gamestate import sdl2_nim/sdl_gpu -# import ../math/mathutils import ../math/vector2 var @@ -62,8 +61,19 @@ proc updateTimeUniform*(this: Shader, time: float) = proc updateResolutionUniform(this: Shader, screenResolution: Vector) = setUniformfv(this.resolutionUniformID, 2, 1, cast[ptr cfloat](resolution.addr)) -proc render*(this: Shader, time: float, screenResolution: Vector) = +proc activate*(this: Shader) = activateShaderProgram(this.programID, this.shaderBlock.addr) - this.updateTimeUniform(time) - this.updateResolutionUniform(screenResolution) + +proc deactivate*(this: Shader) = + deactivateShaderProgram() + +proc render*(this: Shader) = + this.activate() + this.updateTimeUniform(gamestate.runTime) + this.updateResolutionUniform(gamestate.resolution) + +template renderWith*(this: Shader, body: untyped) = + this.render() + body + this.deactivate()