├── .gitignore ├── src └── rapid │ ├── .gitignore │ ├── config.nims │ ├── wrappers │ ├── chipmunk.nim │ ├── chipmunk │ │ ├── compile_chipmunk.nim │ │ ├── simple_motor.nim │ │ ├── gear_joint.nim │ │ ├── rotary_limit_joint.nim │ │ ├── pivot_joint.nim │ │ ├── ratchet_joint.nim │ │ ├── pin_joint.nim │ │ ├── groove_joint.nim │ │ ├── slide_joint.nim │ │ ├── poly_shape.nim │ │ ├── damped_rotary_spring.nim │ │ ├── engine.nim │ │ ├── damped_spring.nim │ │ ├── types.nim │ │ ├── bb.nim │ │ ├── transform.nim │ │ ├── constraint.nim │ │ ├── vect.nim │ │ └── arbiter.nim │ └── freetype.nim │ ├── graphics.nim │ ├── math.nim │ ├── ecs │ ├── components │ │ ├── physics.nim │ │ ├── basic.nim │ │ └── graphics.nim │ ├── physics.nim │ ├── render.nim │ ├── common.nim │ └── system_macro.nim │ ├── ecs.nim │ ├── graphics │ ├── vertex_types.nim │ ├── meshes.nim │ ├── color.nim │ ├── programs.nim │ ├── image.nim │ ├── atlas_texture.nim │ ├── tracers.nim │ ├── context_polyline.nim │ └── postprocess.nim │ ├── math │ ├── util.nim │ ├── rectangle.nim │ ├── units.nim │ ├── interpolation.nim │ └── vector.nim │ ├── game │ ├── state_machine.nim │ └── retro.nim │ ├── game.nim │ ├── algorithm │ └── rect_packer.nim │ └── ec.nim ├── icon.ase ├── logo.ase ├── icon-1x.png ├── icon-8x.png ├── logo-1x.png ├── logo-8x.png ├── tests ├── .gitignore ├── sampleData │ ├── coin1.ogg │ ├── coin2.ogg │ ├── logo-4x.png │ ├── tileset.png │ ├── Lato-Regular.ttf │ ├── Lato-BlackItalic.ttf │ └── OFL.txt ├── config.nims ├── tui.nim ├── tchipmunk.nim ├── tlaser.nim └── tgraphics.nim ├── .gitmodules ├── rapid.nimble ├── LICENSE ├── code_style.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.trace 3 | -------------------------------------------------------------------------------- /src/rapid/.gitignore: -------------------------------------------------------------------------------- 1 | .lite_workspace.lua 2 | -------------------------------------------------------------------------------- /src/rapid/config.nims: -------------------------------------------------------------------------------- 1 | --d:rapidEnableTracers 2 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk.nim: -------------------------------------------------------------------------------- 1 | include chipmunk/engine 2 | -------------------------------------------------------------------------------- /icon.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/icon.ase -------------------------------------------------------------------------------- /logo.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/logo.ase -------------------------------------------------------------------------------- /icon-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/icon-1x.png -------------------------------------------------------------------------------- /icon-8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/icon-8x.png -------------------------------------------------------------------------------- /logo-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/logo-1x.png -------------------------------------------------------------------------------- /logo-8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/logo-8x.png -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | tgraphics 2 | tlaser 3 | tworld 4 | tchipmunk 5 | *.exe 6 | -------------------------------------------------------------------------------- /tests/sampleData/coin1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/coin1.ogg -------------------------------------------------------------------------------- /tests/sampleData/coin2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/coin2.ogg -------------------------------------------------------------------------------- /tests/sampleData/logo-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/logo-4x.png -------------------------------------------------------------------------------- /tests/sampleData/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/tileset.png -------------------------------------------------------------------------------- /tests/sampleData/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/Lato-Regular.ttf -------------------------------------------------------------------------------- /tests/sampleData/Lato-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidev/rapid/HEAD/tests/sampleData/Lato-BlackItalic.ttf -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "../src") 2 | switch("threads", "on") 3 | 4 | when defined(windows): 5 | switch("tlsEmulation", "off") 6 | -------------------------------------------------------------------------------- /src/rapid/graphics.nim: -------------------------------------------------------------------------------- 1 | ## Master module exporting all common graphics modules. 2 | 3 | import graphics/[ 4 | context, 5 | image, 6 | ] 7 | 8 | export context 9 | export image 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/rapid/wrappers/extern/rapid-freetype"] 2 | path = src/rapid/wrappers/extern/freetype 3 | url = https://github.com/liquid600pgm/rapid-freetype.git 4 | [submodule "src/rapid/wrappers/extern/chipmunk"] 5 | path = src/rapid/wrappers/extern/chipmunk 6 | url = https://github.com/slembcke/Chipmunk2D.git 7 | -------------------------------------------------------------------------------- /src/rapid/math.nim: -------------------------------------------------------------------------------- 1 | ## Master module that exports std/math, glm, and rapid's math submodules. 2 | 3 | import std/math 4 | export math 5 | 6 | import glm 7 | export glm 8 | 9 | import math/[ 10 | rectangle, 11 | units, 12 | util, 13 | vector, 14 | ] 15 | export rectangle 16 | export units 17 | export util 18 | export vector 19 | -------------------------------------------------------------------------------- /rapid.nimble: -------------------------------------------------------------------------------- 1 | #~~ 2 | # Package 3 | #~~ 4 | 5 | version = 6 | "2020.1" 7 | author = 8 | "iLiquid" 9 | description = 10 | "A game engine for rapid development and easy prototyping" 11 | license = 12 | "MIT" 13 | srcDir = 14 | "src" 15 | 16 | #~~ 17 | # Dependencies 18 | #~~ 19 | 20 | requires "nim >= 0.20.0" 21 | requires "aglet >= 0.4.2" 22 | requires "stbimage >= 2.5" 23 | -------------------------------------------------------------------------------- /src/rapid/ecs/components/physics.nim: -------------------------------------------------------------------------------- 1 | ## Physics components. 2 | 3 | import glm/vec 4 | 5 | type 6 | PhysicsBody* = object 7 | ## 2D physics body component. Signifies that an entity should have physics 8 | ## simulation. 9 | velocity*, acceleration*: Vec2f 10 | Gravity* = object 11 | ## 2D gravity component. Signifies that the entity has gravity. 12 | force*: Vec2f 13 | -------------------------------------------------------------------------------- /src/rapid/ecs.nim: -------------------------------------------------------------------------------- 1 | ## Master ECS module, imports all the basics needed to get up and running with 2 | ## an ECS world. 3 | 4 | import ecs/[ 5 | system_macro, 6 | ecs_macro, 7 | ] 8 | 9 | export system_macro 10 | export ecs_macro 11 | 12 | # i don't want to throw out good code, so let's just emit a warning 13 | # to any daredevils that try to use this 14 | {.warning: "rapid/ecs is heavily unfinished, use rapid/ec for the time being".} 15 | -------------------------------------------------------------------------------- /src/rapid/ecs/components/basic.nim: -------------------------------------------------------------------------------- 1 | ## Basic, common components. 2 | ## 3 | ## Components should ultimately be nothing but pure data. Due to certain 4 | ## limitations, that isn't always possible, but usage of pointers should be kept 5 | ## to a minimum to aid easy serialization. 6 | 7 | import aglet 8 | 9 | type 10 | Position* = object 11 | ## 2D position component. 12 | position*: Vec2f 13 | Size* = object 14 | ## 2D size component. 15 | size*: Vec2f 16 | -------------------------------------------------------------------------------- /src/rapid/graphics/vertex_types.nim: -------------------------------------------------------------------------------- 1 | import glm/vec 2 | 3 | type 4 | Vertex2dColor* {.packed.} = object 5 | ## Vertex with a position and color. 6 | position*: Vec2f 7 | color*: Vec4f 8 | 9 | Vertex2dUv* {.packed.} = object 10 | ## Vertex with a position and texture coordinates. 11 | position*: Vec2f 12 | uv*: Vec2f 13 | 14 | Vertex2dColorUv* {.packed.} = object 15 | ## Vertex with a position, color, and texture coordinates. 16 | position*: Vec2f 17 | color*: Vec4f 18 | uv*: Vec2f 19 | -------------------------------------------------------------------------------- /src/rapid/ecs/components/graphics.nim: -------------------------------------------------------------------------------- 1 | ## Graphics components. 2 | 3 | import aglet/pixeltypes 4 | 5 | import ../../graphics/context 6 | 7 | type 8 | GraphicColor* = object 9 | ## Color component. 10 | color*: Rgba32f 11 | SpriteGraphic* = object 12 | ## Sprite graphic component. Signifies that an entity should be drawn as a 13 | ## sprite. 14 | graphics*: Graphics 15 | sprite*: Sprite 16 | FillRectGraphic* = object 17 | ## Filled rect shape graphic component. Signifies that an entity should be 18 | ## drawn as a filled rectangle. 19 | -------------------------------------------------------------------------------- /src/rapid/graphics/meshes.nim: -------------------------------------------------------------------------------- 1 | ## Commonly-used meshes. 2 | 3 | import aglet 4 | 5 | import ../math/rectangle 6 | 7 | import vertex_types 8 | 9 | proc uploadRectangle*(mesh: Mesh[Vertex2dUv], position = rectf(-1, -1, 2, 2), 10 | uv = rectf(0, 0, 1, 1)) = 11 | ## Uploads rectangle vertices to the given mesh. 12 | ## This sets the mesh's primitive to a triangle strip. 13 | 14 | mesh.primitive = dpTriangleStrip 15 | mesh.uploadVertices([ 16 | Vertex2dUv(position: position.topLeft, uv: uv.bottomLeft), 17 | Vertex2dUv(position: position.topRight, uv: uv.bottomRight), 18 | Vertex2dUv(position: position.bottomLeft, uv: uv.topLeft), 19 | Vertex2dUv(position: position.bottomRight, uv: uv.topRight), 20 | ]) 21 | -------------------------------------------------------------------------------- /src/rapid/ecs/physics.nim: -------------------------------------------------------------------------------- 1 | ## Physics systems and components. 2 | 3 | import glm/vec 4 | 5 | import components/basic 6 | import components/physics 7 | import system_macro 8 | 9 | export Position, PhysicsBody, Gravity 10 | 11 | system tickPhysics: 12 | ## Ticks the physics: updates the player's position, velocity, and 13 | ## acceleration. 14 | 15 | requires: 16 | var position: Position 17 | var physics: PhysicsBody 18 | 19 | proc update() = 20 | physics.velocity += physics.acceleration 21 | physics.acceleration.reset() 22 | position.position = physics.velocity 23 | 24 | system applyGravity: 25 | ## Applies gravity to entities. 26 | 27 | requires: 28 | var physics: PhysicsBody 29 | let gravity: Gravity 30 | 31 | proc update() = 32 | physics.acceleration += gravity.force 33 | -------------------------------------------------------------------------------- /src/rapid/ecs/render.nim: -------------------------------------------------------------------------------- 1 | ## Rendering-related systems. 2 | 3 | import ../graphics 4 | import components/basic 5 | import components/graphics as comp_graphics 6 | import system_macro 7 | 8 | system drawSprites: 9 | ## Draws sprites from the ``SpriteGraphic`` component. 10 | 11 | requires: 12 | let 13 | position: Position 14 | size: Size 15 | sprite: SpriteGraphic 16 | 17 | proc draw(graphics: Graphics) = 18 | assert graphics == sprite.graphics 19 | graphics.sprite(sprite.sprite, position.position, size.size) 20 | 21 | system drawFillRects: 22 | ## Draws filled rectangles from the ``GraphicColor`` and ``FillRectGraphic`` 23 | ## components. 24 | 25 | requires: 26 | let 27 | position: Position 28 | size: Size 29 | color: GraphicColor 30 | _: FillRectGraphic 31 | 32 | proc draw(graphics: Graphics) = 33 | 34 | -------------------------------------------------------------------------------- /src/rapid/math/util.nim: -------------------------------------------------------------------------------- 1 | ## Various assorted math utilities that don't fit into any other module. 2 | 3 | import std/math 4 | import std/hashes 5 | 6 | import glm/vec 7 | 8 | # ↓ I'd use slices here, but type conversions in Nim are rather funky when 9 | # dealing with generic types, which leads to verbose code containing lots of 10 | # type conversions 11 | func mapRange*[T: SomeFloat](value, min0, max0, min1, max1: T): T {.inline.} = 12 | ## Remaps ``value`` from range ``min0..max0`` to ``min1..max1``. 13 | result = (value - min0) / (max0 - min0) * (max1 - min1) + min1 14 | 15 | func closeTo*[T: SomeFloat](value, epsilon: T): bool {.inline.} = 16 | ## Returns whether ``value`` is close to ``epsilon`` 17 | ## (``value in -epsilon..epsilon``). 18 | value in -epsilon..epsilon 19 | 20 | func quantize*[T: SomeFloat](value, step: T): T {.inline.} = 21 | ## Quantizes ``value`` to ``step``. 22 | step * floor(value / step + 0.5) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 iLiquid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/rapid/game/state_machine.nim: -------------------------------------------------------------------------------- 1 | ## "Stack-based" game state machine. 2 | 3 | type 4 | StateMachine*[S: ref] = object 5 | ## A state machine. ``S`` is a user-provided ref state type. 6 | stack: seq[S] 7 | 8 | proc check[S](sm: var StateMachine[S]) = 9 | 10 | if sm.stack.len == 0: 11 | # can't use nil here for some reason????????? 12 | # in general i've found that nil literals trip the compiler up in some 13 | # cases so there isn't much i can do about this 14 | sm.stack.add(default(S)) 15 | 16 | proc current*[S](sm: var StateMachine[S]): S = 17 | ## Returns the topmost (current) state, or ``nil`` if there are no states. 18 | 19 | check sm 20 | result = sm.stack[^1] 21 | 22 | proc set*[S](sm: var StateMachine[S], newState: S) = 23 | ## Swaps the topmost state to the given state. 24 | 25 | check sm 26 | sm.stack[^1] = newState 27 | 28 | proc push*[S](sm: var StateMachine[S], newState: S) = 29 | ## Pushes a new state onto the stack. 30 | 31 | check sm 32 | if sm.stack[0] == nil: 33 | sm.stack[0] = newState 34 | else: 35 | sm.stack.add(newState) 36 | 37 | proc pop*[S](sm: var StateMachine[S]) = 38 | ## Pops a state off the stack. 39 | 40 | check sm 41 | sm.stack.setLen(sm.len - 1) 42 | -------------------------------------------------------------------------------- /src/rapid/math/rectangle.nim: -------------------------------------------------------------------------------- 1 | ## Extra math oriented around rectangles. 2 | 3 | import aglet/rect 4 | import glm/vec 5 | 6 | export rect 7 | 8 | type 9 | RectangleSide* = enum 10 | ## The side of a rectangle. 11 | rsRight 12 | rsBottom 13 | rsLeft 14 | rsTop 15 | 16 | {.push inline.} 17 | 18 | proc topLeft*[T](rect: Rect[T]): Vec2[T] = 19 | ## Returns the position of the top-left corner of the rectangle. 20 | rect.position 21 | 22 | proc topRight*[T](rect: Rect[T]): Vec2[T] = 23 | ## Returns the position of the top-left corner of the rectangle. 24 | rect.position + vec2(rect.width, T 0) 25 | 26 | proc bottomRight*[T](rect: Rect[T]): Vec2[T] = 27 | ## Returns the position of the top-left corner of the rectangle. 28 | rect.position + rect.size 29 | 30 | proc bottomLeft*[T](rect: Rect[T]): Vec2[T] = 31 | ## Returns the position of the top-left corner of the rectangle. 32 | rect.position + vec2(T 0, rect.height) 33 | 34 | proc rectSides*[T](left, top, right, bottom: T): Rect[T] = 35 | ## Helper for creating a rect with the given sides. 36 | rect[T](left, top, right - left, bottom - top) 37 | 38 | proc intersects*[T](a, b: Rect[T]): bool = 39 | ## Returns whether the two rectangles intersect. 40 | a.left < b.right and b.left < a.right and 41 | a.top < b.bottom and b.top < a.bottom 42 | 43 | {.pop.} 44 | -------------------------------------------------------------------------------- /code_style.md: -------------------------------------------------------------------------------- 1 | # rapid code style guidelines 2 | 3 | This is a set of style guidelines which should be followed by contributors to 4 | the rapid game engine. 5 | 6 | ## 1. Follow NEP 1 7 | 8 | rapid tries to follow NEP 1's coding guidelines. Check them out [here](https://nim-lang.org/docs/nep1.html). 9 | 10 | The following rules take precedence over NEP 1: 11 | 12 | ## 2. Naming conventions 13 | 14 | `const`s should *always* use `PascalCase`. 15 | 16 | ## 3. Styling conventions 17 | 18 | - For name-type pairs, put a single whitespace after the colon: `arg1: int`. 19 | - For calls, always use parentheses, refrain from using the command syntax 20 | unless otherwise specified. In calls, do not put any whitespace before and 21 | after the parentheses. Whenever it makes sense (eg. the object is mutated or 22 | performs some action), use the dot call syntax `a.someProc(b)`. 23 | - For type conversions, use dot call syntax `a.SomeType` whenever possible, 24 | otherwise use either regular proc call syntax `SomeType(a + 2)`. Use command 25 | call syntax `SomeType a` for variable declarations, like `var i = int a / 2`. 26 | - Put whitespace around all operators except `..`, `..<` and `..^`. 27 | - Use `proc ()` instead of `proc()` for anonymous procs. 28 | - Order imports in three sections: system modules, Nimble modules, and own 29 | modules. Order each import in a section alphabetically. 30 | - For other things, NEP 1 rules apply. 31 | 32 | ## 4. Coding conventions 33 | 34 | - Use implicit return *only* in one-line procs for formulas etc., use `result` 35 | everywhere else *unless* you need the flow control capabilities of `return`. 36 | - Use `ref object`s for "portable" objects, that is, objects which are always 37 | passed by reference from proc to proc (like `RGfx`) 38 | -------------------------------------------------------------------------------- /src/rapid/math/units.nim: -------------------------------------------------------------------------------- 1 | ## Mathematical units and type-safe function wrappers. 2 | 3 | import std/macros 4 | import std/math 5 | import std/strutils 6 | 7 | type 8 | Radians* = distinct float32 9 | Degrees* = distinct float32 10 | 11 | proc `$`*(radians: Radians): string {.inline.} = $radians.float32 & " rad" 12 | proc `$`*(degrees: Degrees): string {.inline.} = $degrees.float32 & "°" 13 | 14 | proc `-`*(x: Degrees): Degrees {.borrow.} 15 | proc `+`*(a, b: Degrees): Degrees {.borrow.} 16 | proc `-`*(a, b: Degrees): Degrees {.borrow.} 17 | proc `*`*(a, b: Degrees): Degrees {.borrow.} 18 | proc `/`*(a, b: Degrees): Degrees {.borrow.} 19 | proc `==`*(a, b: Degrees): bool {.borrow.} 20 | proc `<`*(a, b: Degrees): bool {.borrow.} 21 | proc `<=`*(a, b: Degrees): bool {.borrow.} 22 | 23 | proc `-`*(x: Radians): Radians {.borrow.} 24 | proc `+`*(a, b: Radians): Radians {.borrow.} 25 | proc `-`*(a, b: Radians): Radians {.borrow.} 26 | proc `*`*(a, b: Radians): Radians {.borrow.} 27 | proc `/`*(a, b: Radians): Radians {.borrow.} 28 | proc `==`*(a, b: Radians): bool {.borrow.} 29 | proc `<`*(a, b: Radians): bool {.borrow.} 30 | proc `<=`*(a, b: Radians): bool {.borrow.} 31 | 32 | converter toRadians*(degrees: Degrees): Radians {.inline.} = 33 | ## Converter from degrees to radians. 34 | Radians(degrees.float32.degToRad) 35 | 36 | converter toDegrees*(radians: Radians): Degrees {.inline.} = 37 | ## Converter from radians to degrees. 38 | Degrees(radians.float32.radToDeg) 39 | 40 | macro wrapTrig(): untyped = 41 | result = newStmtList() 42 | for baseName in ["sin", "cos", "tan", "cot", "sec", "csc"]: 43 | for deriv in ["$1", "$1h", "arc$1", "arc$1h"]: 44 | let 45 | name = ident(deriv % baseName) 46 | x = ident"x" # prevent x`gensym1283719203709821370973921 ugliness 47 | var doc = newNimNode(nnkCommentStmt) 48 | doc.strVal = 49 | "Type-safe wrapper for ``" & name.repr & "`` trigonometric function." 50 | result.add quote do: 51 | func `name`*(`x`: Radians): float32 {.inline.} = 52 | `doc` 53 | result = `name`(`x`.float32) 54 | wrapTrig 55 | -------------------------------------------------------------------------------- /src/rapid/ecs/common.nim: -------------------------------------------------------------------------------- 1 | ## Common symbols used both by system_macro and ecs_macro. 2 | 3 | import std/macros 4 | import std/tables 5 | 6 | type 7 | SystemImpl* = object 8 | ## System endpoint implementation. 9 | abstract*: NimNode 10 | # abstract, untyped implementation 11 | concrete*: NimNode 12 | # typed implementation with all the requires in params 13 | 14 | SystemInfo* = object 15 | doc*: string 16 | # documentation string 17 | requires*: seq[NimNode] 18 | # list of nnkIdentDefs containing all the required components 19 | implements*: seq[SystemImpl] 20 | # list of nnkProcDef containing all the implemented sysinterface procs 21 | 22 | EntityBase* = uint32 23 | ## Type used for entity ID storage. 24 | 25 | AtEntity* = distinct EntityBase 26 | 27 | proc extractVarType*(maybeVar: NimNode): NimNode = 28 | ## Extracts a type from a ``var`` expression. 29 | 30 | if maybeVar.kind == nnkVarTy: maybeVar[0] 31 | else: maybeVar 32 | 33 | var ecsSystems* {.compileTime.}: Table[string, SystemInfo] 34 | ## Registry storing all the available ECS systems. 35 | 36 | proc getConcrete*(impl: NimNode, sys: SystemInfo, 37 | entityType, worldType: NimNode = nil): NimNode = 38 | ## Gets the concrete implementation of the ``sys``'s endpoint 39 | ## implementation ``impl``. 40 | 41 | result = copy(impl) 42 | if result[0].kind == nnkPostfix: 43 | hint("export marker is unnecessary", result[0][0]) 44 | result[0] = result[0][1] 45 | result.addPragma(ident"used") 46 | result.addPragma(ident"nimcall") # force it to be non-closure 47 | for req in sys.requires: 48 | let 49 | name = req[0] 50 | fullType = req[1].extractVarType() 51 | typeSym = 52 | if fullType.eqIdent("@entity"): 53 | if entityType != nil: entityType 54 | else: bindSym"AtEntity" 55 | elif fullType.eqIdent("@world"): 56 | if worldType != nil: worldType 57 | else: return nil 58 | else: fullType 59 | ty = 60 | if req[1].kind == nnkVarTy: newTree(nnkVarTy, typeSym) 61 | else: typeSym 62 | result.params.add(newIdentDefs(name, ty)) 63 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/compile_chipmunk.nim: -------------------------------------------------------------------------------- 1 | ## This module compiles Chipmunk2D without using CMake. 2 | ## Included by ``wrappers/chipmunk``, do not use directly. 3 | 4 | import std/macros 5 | import std/os 6 | 7 | const 8 | Parent = currentSourcePath.splitPath().head & "/.." 9 | Freetype = Parent & "/extern/chipmunk" 10 | Include = Freetype & "/include" 11 | Src = Freetype & "/src" 12 | {.passC: "-I" & Include.} 13 | 14 | # TODO: remove this passC bloat mess when nim 1.4 is released 15 | 16 | # set up types 17 | 18 | when defined(rapidChipmunkUseFloat64): 19 | {.passC: "-DCP_USE_DOUBLES=1".} 20 | else: 21 | {.passC: "-DCP_USE_DOUBLES=0".} 22 | 23 | {.passC: "-DCP_COLLISION_TYPE_TYPE=uint16_t".} 24 | {.passC: "-DCP_BITMASK_TYPE=uint64_t".} 25 | 26 | # disable debug messages because they're annoying 27 | # this also disables runtime assertions which is a bit trash but i don't want 28 | # chipmunk spamming my console output 29 | # DEAR LIBRARY DEVELOPERS: DON'T WRITE TO STDOUT IN YOUR LIBRARIES. 30 | # SIGNED, LQDEV 31 | # 11 OCTOBER 2020 32 | 33 | {.passC: "-DNDEBUG".} 34 | 35 | macro genCompiles: untyped = 36 | var 37 | compileList = @[ 38 | "chipmunk.c", 39 | "cpArbiter.c", 40 | "cpArray.c", 41 | "cpBBTree.c", 42 | "cpBody.c", 43 | "cpCollision.c", 44 | "cpConstraint.c", 45 | "cpDampedRotarySpring.c", 46 | "cpDampedSpring.c", 47 | "cpGearJoint.c", 48 | "cpGrooveJoint.c", 49 | "cpHashSet.c", 50 | "cpMarch.c", 51 | "cpPinJoint.c", 52 | "cpPivotJoint.c", 53 | "cpPolyShape.c", 54 | "cpPolyline.c", 55 | "cpRatchetJoint.c", 56 | "cpRobust.c", 57 | "cpRotaryLimitJoint.c", 58 | "cpShape.c", 59 | "cpSimpleMotor.c", 60 | "cpSlideJoint.c", 61 | "cpSpace.c", 62 | "cpSpaceComponent.c", 63 | "cpSpaceDebug.c", 64 | "cpSpaceHash.c", 65 | "cpSpaceQuery.c", 66 | "cpSpaceStep.c", 67 | "cpSpatialIndex.c", 68 | "cpSweep1D.c", 69 | ] 70 | when compileOption("threads"): 71 | compileList.add "cpHastySpace.c" 72 | var pragmas = newNimNode(nnkPragma) 73 | for file in compileList: 74 | pragmas.add(newColonExpr(ident"compile", newLit(Src & "/" & file))) 75 | result = newStmtList(pragmas) 76 | genCompiles 77 | -------------------------------------------------------------------------------- /src/rapid/graphics/color.nim: -------------------------------------------------------------------------------- 1 | ## Color handling utilities. 2 | 3 | import std/colors 4 | import std/strutils 5 | 6 | import aglet/pixeltypes 7 | 8 | export colors except Color, rgb 9 | 10 | type 11 | Color* = Rgba32f 12 | ## Alias for colors. 13 | 14 | converter rgba32f*(color: colors.Color): Color {.inline.} = 15 | ## Converts an stdlib color to a rapid color. 16 | 17 | let (r, g, b) = color.extractRgb 18 | result = rgba32f(r / 255, g / 255, b / 255, 1) 19 | 20 | proc hex*(hex: string): Color = 21 | ## Parses a hexadecimal color. 22 | ## 23 | ## Syntax: 24 | ## 25 | ## .. code-block:: 26 | ## hexDigit <- [0-9a-fA-F] 27 | ## color <- '#'? (hexDigit{6} hexDigit{2}? / hexDigit{3} hexDigit?) 28 | ## 29 | ## Examples: 30 | ## 31 | ## - ``ffffff`` == ``fff`` 32 | ## - ``#ffffff`` == ``#fff`` 33 | ## - ``#09aaff`` 34 | ## - ``#1af`` == ``#11aaff`` 35 | ## - ``00000033`` == ``0003`` 36 | 37 | var hex = hex 38 | if hex[0] == '#': 39 | hex = hex[1..^1] 40 | 41 | assert hex.len in {3, 4, 6, 8}, 42 | "hex color must be formatted as #RGB, #RGBA, #RRGGBB, or #RRGGBBAA" 43 | 44 | var r, g, b, a: int 45 | 46 | case hex.len 47 | of 3, 4: 48 | r = parseHexInt(hex[0] & hex[0]) 49 | g = parseHexInt(hex[1] & hex[1]) 50 | b = parseHexInt(hex[2] & hex[2]) 51 | a = 52 | if hex.len == 4: parseHexInt(hex[3] & hex[3]) 53 | else: 255 54 | of 6, 8: 55 | r = parseHexInt(hex[0..1]) 56 | g = parseHexInt(hex[2..3]) 57 | b = parseHexInt(hex[4..5]) 58 | a = 59 | if hex.len == 8: parseHexInt(hex[6..7]) 60 | else: 255 61 | else: doAssert false, "unreachable" 62 | 63 | result = rgba(r / 255, g / 255, b / 255, a / 255) 64 | 65 | template hex*(lit: static string): Color = 66 | ## Optimization for parsing constant colors at compile time. 67 | 68 | const compileTimeColor = hex(lit) 69 | compileTimeColor 70 | 71 | proc withRed*(color: Color, red: float32): Color = 72 | ## Copies the color with a different red channel. 73 | rgba(red, color.g, color.b, color.a) 74 | 75 | proc withGreen*(color: Color, green: float32): Color = 76 | ## Copies the color with a different green channel. 77 | rgba(color.r, green, color.b, color.a) 78 | 79 | proc withBlue*(color: Color, blue: float32): Color = 80 | ## Copies the color with a different blue channel. 81 | rgba(color.r, color.g, blue, color.a) 82 | 83 | proc withAlpha*(color: Color, alpha: float32): Color = 84 | ## Copies the color with a different alpha channel. 85 | rgba(color.r, color.g, color.b, alpha) 86 | -------------------------------------------------------------------------------- /src/rapid/math/interpolation.nim: -------------------------------------------------------------------------------- 1 | #-- 2 | # rapid 3 | # a game engine optimized for rapid prototyping 4 | # copyright (c) 2019, iLiquid 5 | # licensed under the MIT license - see LICENSE file for more information 6 | #-- 7 | 8 | ## This module implements interpolation math, including step, linear, and cubic 9 | ## Hermite spline curves. 10 | 11 | import math 12 | import glm/vec 13 | 14 | 15 | # basics 16 | 17 | func lerp*[T](a, b: T, t: SomeFloat): T {.inline.} = 18 | ## Fast linear interpolation. 19 | t.T * b + T(1 - t) * a 20 | 21 | func lerp*[T, N](a, b: Vec[N, T], t: SomeFloat): Vec[N, T] {.inline.} = 22 | ## Linear interpolation for vectors. 23 | 24 | for i, u in a.arr: 25 | let v = b.arr[i] 26 | result.arr[i] = lerp(u, v, t) 27 | 28 | 29 | # interpolators 30 | 31 | type 32 | Interpolated*[T] = object 33 | prev, curr: T 34 | 35 | {.push inline.} 36 | 37 | converter interpolated*[T](value: T): Interpolated[T] = 38 | ## Converter from values to ``Interpolated[T]``. 39 | Interpolated[T](prev: value, curr: value) 40 | 41 | converter value*[T](interp: Interpolated[T]): T = 42 | ## Converter from ``Interpolated[T]`` to values. 43 | interp.curr 44 | 45 | converter mvalue*[T](interp: var Interpolated[T]): var T = 46 | ## "Dereference" operator for ``Interpolated[T]`` 47 | interp.curr 48 | 49 | func lerp*[T](interp: Interpolated[T], t: SomeFloat): T = 50 | ## Linearly interpolates between the previous and current value of the 51 | ## ``Interpolated[T]``. 52 | result = lerp(interp.prev, interp.curr, t) 53 | 54 | proc tick*[T](interp: var Interpolated[T]) = 55 | ## Updates the given ``Interpolated[T]``'s previous value with the current 56 | ## value. This is usually called in ``update``. 57 | interp.prev = interp.curr 58 | 59 | proc tickInterpolated*[T: tuple | object](rec: var T) = 60 | ## Ticks all ``Interpolated[T]`` in the given object. 61 | 62 | for value in fields(rec): 63 | when value is Interpolated: 64 | value.tick() 65 | 66 | proc `<-`*[T](interp: var Interpolated[T], value: T) = 67 | ## Shortcut operator for assigning to ``mvalue(interp)``. 68 | interp.curr = value 69 | 70 | proc `<-+`*[T](interp: var Interpolated[T], x: T) = 71 | ## Shortcut for ``mvalue(interp) += x``. 72 | interp.curr += x 73 | 74 | proc `<--`*[T](interp: var Interpolated[T], x: T) = 75 | ## Shortcut for ``mvalue(interp) -= x``. 76 | interp.curr -= x 77 | 78 | proc `<-*`*[T](interp: var Interpolated[T], x: T) = 79 | ## Shortcut for ``mvalue(interp) *= x``. 80 | interp.curr *= x 81 | 82 | proc `<-/`*[T](interp: var Interpolated[T], x: T) = 83 | ## Shortcut for ``mvalue(interp) /= x``. 84 | interp.curr /= x 85 | 86 | {.pop.} 87 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/simple_motor.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpSimpleMotor cpSimpleMotor 22 | ## / @{ 23 | ## / Opaque struct type for damped rotary springs. 24 | 25 | type 26 | cpSimpleMotor* {.importc, incompleteStruct.} = object 27 | 28 | ## / Check if a constraint is a damped rotary springs. 29 | 30 | proc cpConstraintIsSimpleMotor*(constraint: ptr cpConstraint): cpBool {. 31 | importc: "cpConstraintIsSimpleMotor", header: "".} 32 | ## / Allocate a simple motor. 33 | 34 | proc cpSimpleMotorAlloc*(): ptr cpSimpleMotor {.importc: "cpSimpleMotorAlloc", 35 | header: "".} 36 | ## / initialize a simple motor. 37 | 38 | proc cpSimpleMotorInit*(joint: ptr cpSimpleMotor; a: ptr cpBody; b: ptr cpBody; 39 | rate: cpFloat): ptr cpSimpleMotor {. 40 | importc: "cpSimpleMotorInit", header: "".} 41 | ## / Allocate and initialize a simple motor. 42 | 43 | proc cpSimpleMotorNew*(a: ptr cpBody; b: ptr cpBody; rate: cpFloat): ptr cpConstraint {. 44 | importc: "cpSimpleMotorNew", header: "".} 45 | ## / Get the rate of the motor. 46 | 47 | proc cpSimpleMotorGetRate*(constraint: ptr cpConstraint): cpFloat {. 48 | importc: "cpSimpleMotorGetRate", header: "".} 49 | ## / Set the rate of the motor. 50 | 51 | proc cpSimpleMotorSetRate*(constraint: ptr cpConstraint; rate: cpFloat) {. 52 | importc: "cpSimpleMotorSetRate", header: "".} 53 | ## / @} 54 | -------------------------------------------------------------------------------- /src/rapid/game.nim: -------------------------------------------------------------------------------- 1 | ## Things essential for any game out there. This module also has a few 2 | ## submodules for things that didn't fit into any other category: 3 | ## 4 | ## - ``rapid/game/tilemap`` – tilemap with collision detection 5 | 6 | import std/monotimes 7 | import std/times 8 | 9 | template runGameWhile*(cond: bool, body: untyped, 10 | updateFreq: float32 = 60): untyped = 11 | ## Starts a fixed timestep game loop. 12 | ## There are two important templates available inside the loop: 13 | ## 14 | ## - ``update: `` – anything inside the `` runs at a constant 15 | ## tick rate of ``updateFreq`` 16 | ## - ``draw : `` – calculates the interpolation coefficient 17 | ## `` and passes it to the `` 18 | ## 19 | ## There are also a few variables available inside the loop: 20 | ## 21 | ## - ``time: float64`` – time elapsed since the start of the loop, in seconds 22 | ## - ``delta: float64`` – time elapsed since the last frame, in seconds 23 | ## - ``secondsPerUpdate: float32`` – time between updates 24 | ## 25 | ## **Example:** 26 | ## 27 | ## .. code-block:: nim 28 | ## 29 | ## import std/os 30 | ## 31 | ## runGameWhile true: # the condition is usually ``not win.closeRequested`` 32 | ## echo (time: time, delta: delta) 33 | ## 34 | ## # process input here 35 | ## 36 | ## update: 37 | ## # tick your entities here 38 | ## 39 | ## draw step: 40 | ## # draw your entities here 41 | ## 42 | ## sleep(20) # this is fulfilled by ``frame.finish()``, but we don't have 43 | ## # a window in this example 44 | 45 | block: 46 | 47 | var 48 | startTime = getMonoTime() 49 | previousTime = getMonoTime() 50 | lag: float32 = 0.0 51 | 52 | while cond: 53 | let 54 | currentTime = getMonoTime() 55 | delta {.inject.} = 56 | float32(inMilliseconds(currentTime - previousTime).float64 * 0.001) 57 | previousTime = currentTime 58 | lag += delta 59 | 60 | let 61 | time {.inject.} = 62 | inMilliseconds(currentTime - startTime).float64 * 0.001 63 | secondsPerUpdate {.inject.} = 1'f32 / updateFreq 64 | 65 | template update(updateBody: untyped): untyped {.inject.} = 66 | 67 | block: 68 | while lag >= secondsPerUpdate: 69 | updateBody 70 | lag -= secondsPerUpdate 71 | 72 | template draw(stepName, drawBody: untyped): untyped {.inject.} = 73 | 74 | block: 75 | let stepName {.inject.} = lag / secondsPerUpdate 76 | drawBody 77 | 78 | body 79 | 80 | when isMainModule: 81 | 82 | import std/os 83 | 84 | runGameWhile true: 85 | echo (time: time, delta: delta) 86 | update: 87 | echo "running update" 88 | draw step: 89 | echo "drawing with step ", step 90 | sleep(20) 91 | -------------------------------------------------------------------------------- /tests/tui.nim: -------------------------------------------------------------------------------- 1 | import aglet 2 | import aglet/window/glfw 3 | import rapid/game 4 | import rapid/graphics 5 | import rapid/ui 6 | 7 | import unicode 8 | 9 | const 10 | black = hex"#181818" 11 | cherry = hex"#ED124D" 12 | transparent = rgba(0, 0, 0, 0) 13 | 14 | template button(ui: Ui, size: Vec2f, label: string, action: untyped) = 15 | 16 | ui.box(size, blFreeform): 17 | ui.outline(cherry) 18 | ui.color = transparent 19 | 20 | ui.mouseHover(): ui.color = cherry.withAlpha(0.2) 21 | ui.mouseDown(mbLeft): ui.color = cherry.withAlpha(0.4) 22 | 23 | ui.mouseReleased(mbLeft): 24 | `action` 25 | 26 | ui.fill() 27 | 28 | ui.text(label, color = black, alignment = (apCenter, apMiddle)) 29 | 30 | type 31 | Slider = object 32 | pressed: bool 33 | value: float32 34 | 35 | converter value(slider: Slider): float32 = slider.value 36 | 37 | template slider(ui: Ui, bwidth: float32, slider: var Slider) = 38 | 39 | ui.box(vec2f(bwidth, 24), blFreeform): 40 | ui.drawInBox: 41 | let x = 6 + slider.value * (bwidth - 12) 42 | let y = ui.height / 2 43 | ui.graphics.line(vec2f(0, y), vec2f(ui.width, y), thickness = 2, 44 | colorA = cherry, colorB = cherry) 45 | ui.graphics.circle(x, 12, radius = 6, color = cherry) 46 | ui.mouseDown(mbLeft): 47 | let x = ui.mousePosition.x / (bwidth - 1) 48 | slider.value = x 49 | 50 | proc main() = 51 | 52 | var aglet = initAglet() 53 | aglet.initWindow() 54 | 55 | const LatoTtf = slurp("sampleData/Lato-Regular.ttf") 56 | 57 | var 58 | window = aglet.newWindowGlfw(800, 600, "rapid/ui", 59 | winHints(msaaSamples = 8)) 60 | ui = window.newUi() 61 | font = ui.graphics.newFont(LatoTtf, 14) 62 | 63 | ui.graphics.defaultDrawParams = ui.graphics.defaultDrawParams.derive: 64 | multisample on 65 | 66 | var 67 | slider = Slider(value: 0.5) 68 | 69 | runGameWhile not window.closeRequested: 70 | 71 | window.pollEvents proc (event: InputEvent) = 72 | ui.processEvent(event) 73 | 74 | update: discard 75 | 76 | draw step: 77 | var frame = window.render() 78 | frame.clearColor(colWhite) 79 | 80 | ui.begin(frame) 81 | ui.font = font 82 | 83 | ui.box(ui.size, blVertical): 84 | ui.pad(16) 85 | ui.spacing = 8 86 | ui.fontHeight = 14 87 | 88 | ui.box(size = vec2f(ui.width, 32), blHorizontal): 89 | ui.spacing = 8 90 | for i in 1..5: 91 | ui.button(size = vec2f(32, 32), label = $i): 92 | echo "clicked: ", i 93 | 94 | ui.button(size = vec2f(128, 32), label = "Hello"): 95 | echo "hello" 96 | 97 | ui.slider(256, slider) 98 | 99 | ui.draw(frame) 100 | 101 | frame.finish() 102 | 103 | when isMainModule: main() 104 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/gear_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpGearJoint cpGearJoint 22 | ## / @{ 23 | ## / Check if a constraint is a damped rotary springs. 24 | 25 | proc cpConstraintIsGearJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsGearJoint", header: "".} 27 | ## / Allocate a gear joint. 28 | 29 | proc cpGearJointAlloc*(): ptr cpGearJoint {.importc: "cpGearJointAlloc", 30 | header: "".} 31 | ## / Initialize a gear joint. 32 | 33 | proc cpGearJointInit*(joint: ptr cpGearJoint; a: ptr cpBody; b: ptr cpBody; 34 | phase: cpFloat; ratio: cpFloat): ptr cpGearJoint {. 35 | importc: "cpGearJointInit", header: "".} 36 | ## / Allocate and initialize a gear joint. 37 | 38 | proc cpGearJointNew*(a: ptr cpBody; b: ptr cpBody; phase: cpFloat; ratio: cpFloat): ptr cpConstraint {. 39 | importc: "cpGearJointNew", header: "".} 40 | ## / Get the phase offset of the gears. 41 | 42 | proc cpGearJointGetPhase*(constraint: ptr cpConstraint): cpFloat {. 43 | importc: "cpGearJointGetPhase", header: "".} 44 | ## / Set the phase offset of the gears. 45 | 46 | proc cpGearJointSetPhase*(constraint: ptr cpConstraint; phase: cpFloat) {. 47 | importc: "cpGearJointSetPhase", header: "".} 48 | ## / Get the angular distance of each ratchet. 49 | 50 | proc cpGearJointGetRatio*(constraint: ptr cpConstraint): cpFloat {. 51 | importc: "cpGearJointGetRatio", header: "".} 52 | ## / Set the ratio of a gear joint. 53 | 54 | proc cpGearJointSetRatio*(constraint: ptr cpConstraint; ratio: cpFloat) {. 55 | importc: "cpGearJointSetRatio", header: "".} 56 | ## / @} 57 | -------------------------------------------------------------------------------- /src/rapid/game/retro.nim: -------------------------------------------------------------------------------- 1 | ## A fixed-size pixel canvas that scales to the size of the window. 2 | 3 | import aglet 4 | 5 | import ../graphics/meshes 6 | import ../graphics/programs 7 | import ../graphics/vertex_types 8 | 9 | type 10 | Retro* = ref object 11 | window: Window 12 | canvas: Texture2D[Rgba8] 13 | framebuffer: SimpleFramebuffer 14 | ## this framebuffer always has depth and stencil attachments 15 | 16 | program: Program[Vertex2dUv] 17 | rect: Mesh[Vertex2dUv] 18 | drawParams: DrawParams 19 | 20 | scale: Positive 21 | position: Vec2f 22 | 23 | proc newRetro*(window: Window, size: Vec2i): Retro = 24 | ## Creates a new retro canvas with the given size. 25 | 26 | result = Retro(scale: 1) 27 | result.window = window 28 | result.canvas = window.newTexture2D[:Rgba8](size) 29 | 30 | var renbuf = window.newRenderbuffer[:Depth24Stencil8](size) 31 | result.framebuffer = window.newFramebuffer(result.canvas, renbuf) 32 | 33 | result.program = window.programProjection(Vertex2dUv) 34 | result.rect = window.newMesh[:Vertex2dUv](muStatic, dpTriangleStrip) 35 | result.drawParams = defaultDrawParams() 36 | 37 | {.push inline.} 38 | 39 | proc size*(retro: Retro): Vec2i = 40 | ## Returns the size of the retro canvas as a vector. 41 | retro.canvas.size 42 | 43 | proc width*(retro: Retro): int = 44 | ## Returns the width of the retro canvas. 45 | retro.size.x 46 | 47 | proc height*(retro: Retro): int = 48 | ## Returns the height of the retro canvas. 49 | retro.size.y 50 | 51 | proc onScreenPosition*(retro: Retro): Vec2f = 52 | ## Returns the on-screen position of the retro. 53 | retro.position 54 | 55 | proc onScreenSize*(retro: Retro): Vec2f = 56 | ## Returns the on-screen size of the retro. 57 | retro.size.vec2f * retro.scale.float32 58 | 59 | proc onScreenWidth*(retro: Retro): float32 = 60 | ## Returns the on-screen width of the retro. 61 | retro.onScreenSize.x 62 | 63 | proc onScreenHeight*(retro: Retro): float32 = 64 | ## Returns the on-screen height of the retro. 65 | retro.onScreenSize.y 66 | 67 | proc render*(retro: Retro): FramebufferTarget = 68 | ## Returns the rendering target of the retro's framebuffer. 69 | retro.framebuffer.render() 70 | 71 | proc recalculateScaleAndPosition(retro: Retro) = 72 | ## Recalculates the scale and position of the retro canvas. 73 | 74 | let (w, h) = (retro.window.width, retro.window.height) 75 | retro.scale = max(1, min(w div retro.width, h div retro.height)) 76 | retro.position = retro.window.size.vec2f / 2 - retro.onScreenSize / 2 77 | 78 | {.pop.} 79 | 80 | proc draw*(retro: Retro, target: Target) = 81 | ## Calculates the correct position for rendering and draws the retro canvas 82 | ## onto the given target. 83 | 84 | retro.recalculateScaleAndPosition() 85 | retro.rect.uploadRectangle(rectf(retro.position, retro.onScreenSize)) 86 | 87 | let (w, h) = (retro.window.width.float32, retro.window.height.float32) 88 | target.draw(retro.program, retro.rect, uniforms [used] { 89 | projection: ortho(0f, w, h, 0f, -1f, 1f), 90 | sampler: retro.framebuffer.sampler(magFilter = fmNearest), 91 | }, retro.drawParams) 92 | -------------------------------------------------------------------------------- /src/rapid/math/vector.nim: -------------------------------------------------------------------------------- 1 | ## Extra vector math. 2 | 3 | import std/hashes 4 | import std/math 5 | 6 | import glm/vec 7 | 8 | import units 9 | import util 10 | 11 | func perpClockwise*[T](v: Vec2[T]): Vec2[T] {.inline.} = 12 | ## Returns a vector clockwise perpendicular to ``v``. 13 | vec2(v.y, -v.x) 14 | 15 | func perpCounterClockwise*[T](v: Vec2[T]): Vec2[T] {.inline.} = 16 | ## Returns a vector counter-clockwise perpendicular to ``v``. 17 | vec2(-v.y, v.x) 18 | 19 | func angle*[T: SomeFloat](v: Vec2[T]): Radians {.inline.} = 20 | ## Returns the angle of the vector from (0, 0) to (v.x, v.y). 21 | arctan2(v.y, v.x).Radians 22 | 23 | func toVector*(angle: Radians): Vec2f {.inline.} = 24 | ## Converts an angle to a vector. 25 | vec2f(cos(angle), sin(angle)) 26 | 27 | type 28 | IntersectResult* = enum 29 | irParallel ## the lines are parallel 30 | irCoincide ## the lines concide 31 | irInsideBoth ## the intersection lies inside both segments 32 | irOutside0 ## the intersection lies outside segment 0 33 | irOutside1 ## the intersection lies outside segment 1 34 | irOutsideBoth ## the intersection lies outside both segments 35 | 36 | func lineIntersect*(a0, b0, a1, b1: Vec2f): (IntersectResult, Vec2f) = 37 | ## Calculates the intersection point of two lines. 38 | 39 | # actually I have no idea how this algorithm works 40 | # literally translated it from C from that polyline tesselation article 41 | # (see graphics/context_polyline.nim for link) 42 | 43 | const Epsilon = 0.0000001 44 | 45 | let 46 | den = (b1.y - a1.y) * (b0.x - a0.x) - (b1.x - a1.x) * (b0.y - a0.y) 47 | numA = (b1.x - a1.x) * (a0.y - a1.y) - (b1.y - a1.y) * (a0.x - a1.x) 48 | numB = (b0.x - a0.x) * (a0.y - a1.y) - (b0.y - a0.y) * (a0.x - a1.x) 49 | 50 | if numA.closeTo(Epsilon) and numB.closeTo(Epsilon) and den.closeTo(Epsilon): 51 | return (irCoincide, (a0 + b0) * 0.5) 52 | 53 | if den.closeTo(Epsilon): 54 | return (irParallel, vec2f(0)) 55 | 56 | let 57 | muA = numA / den 58 | muB = numB / den 59 | result[1] = a0 + muA * (b0 - a0) 60 | 61 | let 62 | out1 = muA notin 0.0..1.0 63 | out2 = muB notin 0.0..1.0 64 | 65 | result[0] = 66 | if out1 and out2: irOutsideBoth 67 | elif out1: irOutside0 68 | elif out2: irOutside1 69 | else: irInsideBoth 70 | 71 | func angleBetweenLines*(a0, b0, a1, b1: Vec2f): Radians {.inline.} = 72 | ## Returns the angle between the lines (a0, b0) and (a1, b1). 73 | let 74 | da = a1 - a0 75 | db = b1 - b0 76 | result = arctan2(da.x * db.y - da.y * db.x, dot(da, db)).Radians 77 | 78 | func bisector*[T](anchor, a, b: Vec2[T]): Vec2[T] {.inline.} = 79 | ## Calculates the bisector of the angle in the corner ``a, anchor, b``, and 80 | ## returns it as a vector. 81 | let 82 | normA = normalize(a - anchor) 83 | normB = normalize(b - anchor) 84 | bisectorVector = normA + normB 85 | if bisectorVector == vec2(T 0): 86 | # avoid division by zero 87 | normB.perpCounterClockwise 88 | else: 89 | normalize(bisectorVector) 90 | 91 | func hash*[N, T](vec: Vec[N, T]): Hash = 92 | ## Returns the hash of the vector. 93 | var h: Hash 94 | for x in vec.arr: 95 | h = h !& hash(x) 96 | !$h 97 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/rotary_limit_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpRotaryLimitJoint cpRotaryLimitJoint 22 | ## / @{ 23 | ## / Check if a constraint is a damped rotary springs. 24 | 25 | proc cpConstraintIsRotaryLimitJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsRotaryLimitJoint", header: "".} 27 | ## / Allocate a damped rotary limit joint. 28 | 29 | proc cpRotaryLimitJointAlloc*(): ptr cpRotaryLimitJoint {. 30 | importc: "cpRotaryLimitJointAlloc", header: "".} 31 | ## / Initialize a damped rotary limit joint. 32 | 33 | proc cpRotaryLimitJointInit*(joint: ptr cpRotaryLimitJoint; a: ptr cpBody; 34 | b: ptr cpBody; min: cpFloat; max: cpFloat): ptr cpRotaryLimitJoint {. 35 | importc: "cpRotaryLimitJointInit", header: "".} 36 | ## / Allocate and initialize a damped rotary limit joint. 37 | 38 | proc cpRotaryLimitJointNew*(a: ptr cpBody; b: ptr cpBody; min: cpFloat; max: cpFloat): ptr cpConstraint {. 39 | importc: "cpRotaryLimitJointNew", header: "".} 40 | ## / Get the minimum distance the joint will maintain between the two anchors. 41 | 42 | proc cpRotaryLimitJointGetMin*(constraint: ptr cpConstraint): cpFloat {. 43 | importc: "cpRotaryLimitJointGetMin", header: "".} 44 | ## / Set the minimum distance the joint will maintain between the two anchors. 45 | 46 | proc cpRotaryLimitJointSetMin*(constraint: ptr cpConstraint; min: cpFloat) {. 47 | importc: "cpRotaryLimitJointSetMin", header: "".} 48 | ## / Get the maximum distance the joint will maintain between the two anchors. 49 | 50 | proc cpRotaryLimitJointGetMax*(constraint: ptr cpConstraint): cpFloat {. 51 | importc: "cpRotaryLimitJointGetMax", header: "".} 52 | ## / Set the maximum distance the joint will maintain between the two anchors. 53 | 54 | proc cpRotaryLimitJointSetMax*(constraint: ptr cpConstraint; max: cpFloat) {. 55 | importc: "cpRotaryLimitJointSetMax", header: "".} 56 | ## / @} 57 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/pivot_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpPivotJoint cpPivotJoint 22 | ## / @{ 23 | ## / Check if a constraint is a slide joint. 24 | 25 | proc cpConstraintIsPivotJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsPivotJoint", header: "".} 27 | ## / Allocate a pivot joint 28 | 29 | proc cpPivotJointAlloc*(): ptr cpPivotJoint {.importc: "cpPivotJointAlloc", 30 | header: "".} 31 | ## / Initialize a pivot joint. 32 | 33 | proc cpPivotJointInit*(joint: ptr cpPivotJoint; a: ptr cpBody; b: ptr cpBody; 34 | anchorA: cpVect; anchorB: cpVect): ptr cpPivotJoint {. 35 | importc: "cpPivotJointInit", header: "".} 36 | ## / Allocate and initialize a pivot joint. 37 | 38 | proc cpPivotJointNew*(a: ptr cpBody; b: ptr cpBody; pivot: cpVect): ptr cpConstraint {. 39 | importc: "cpPivotJointNew", header: "".} 40 | ## / Allocate and initialize a pivot joint with specific anchors. 41 | 42 | proc cpPivotJointNew2*(a: ptr cpBody; b: ptr cpBody; anchorA: cpVect; anchorB: cpVect): ptr cpConstraint {. 43 | importc: "cpPivotJointNew2", header: "".} 44 | ## / Get the location of the first anchor relative to the first body. 45 | 46 | proc cpPivotJointGetAnchorA*(constraint: ptr cpConstraint): cpVect {. 47 | importc: "cpPivotJointGetAnchorA", header: "".} 48 | ## / Set the location of the first anchor relative to the first body. 49 | 50 | proc cpPivotJointSetAnchorA*(constraint: ptr cpConstraint; anchorA: cpVect) {. 51 | importc: "cpPivotJointSetAnchorA", header: "".} 52 | ## / Get the location of the second anchor relative to the second body. 53 | 54 | proc cpPivotJointGetAnchorB*(constraint: ptr cpConstraint): cpVect {. 55 | importc: "cpPivotJointGetAnchorB", header: "".} 56 | ## / Set the location of the second anchor relative to the second body. 57 | 58 | proc cpPivotJointSetAnchorB*(constraint: ptr cpConstraint; anchorB: cpVect) {. 59 | importc: "cpPivotJointSetAnchorB", header: "".} 60 | ## / @} 61 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/ratchet_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpRatchetJoint cpRatchetJoint 22 | ## / @{ 23 | ## / Check if a constraint is a damped rotary springs. 24 | 25 | proc cpConstraintIsRatchetJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsRatchetJoint", header: "".} 27 | ## / Allocate a ratchet joint. 28 | 29 | proc cpRatchetJointAlloc*(): ptr cpRatchetJoint {.importc: "cpRatchetJointAlloc", 30 | header: "".} 31 | ## / Initialize a ratched joint. 32 | 33 | proc cpRatchetJointInit*(joint: ptr cpRatchetJoint; a: ptr cpBody; b: ptr cpBody; 34 | phase: cpFloat; ratchet: cpFloat): ptr cpRatchetJoint {. 35 | importc: "cpRatchetJointInit", header: "".} 36 | ## / Allocate and initialize a ratchet joint. 37 | 38 | proc cpRatchetJointNew*(a: ptr cpBody; b: ptr cpBody; phase: cpFloat; ratchet: cpFloat): ptr cpConstraint {. 39 | importc: "cpRatchetJointNew", header: "".} 40 | ## / Get the angle of the current ratchet tooth. 41 | 42 | proc cpRatchetJointGetAngle*(constraint: ptr cpConstraint): cpFloat {. 43 | importc: "cpRatchetJointGetAngle", header: "".} 44 | ## / Set the angle of the current ratchet tooth. 45 | 46 | proc cpRatchetJointSetAngle*(constraint: ptr cpConstraint; angle: cpFloat) {. 47 | importc: "cpRatchetJointSetAngle", header: "".} 48 | ## / Get the phase offset of the ratchet. 49 | 50 | proc cpRatchetJointGetPhase*(constraint: ptr cpConstraint): cpFloat {. 51 | importc: "cpRatchetJointGetPhase", header: "".} 52 | ## / Get the phase offset of the ratchet. 53 | 54 | proc cpRatchetJointSetPhase*(constraint: ptr cpConstraint; phase: cpFloat) {. 55 | importc: "cpRatchetJointSetPhase", header: "".} 56 | ## / Get the angular distance of each ratchet. 57 | 58 | proc cpRatchetJointGetRatchet*(constraint: ptr cpConstraint): cpFloat {. 59 | importc: "cpRatchetJointGetRatchet", header: "".} 60 | ## / Set the angular distance of each ratchet. 61 | 62 | proc cpRatchetJointSetRatchet*(constraint: ptr cpConstraint; ratchet: cpFloat) {. 63 | importc: "cpRatchetJointSetRatchet", header: "".} 64 | ## / @} 65 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/pin_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpPinJoint cpPinJoint 22 | ## / @{ 23 | ## / Check if a constraint is a pin joint. 24 | 25 | proc cpConstraintIsPinJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsPinJoint", header: "".} 27 | ## / Allocate a pin joint. 28 | 29 | proc cpPinJointAlloc*(): ptr cpPinJoint {.importc: "cpPinJointAlloc", 30 | header: "".} 31 | ## / Initialize a pin joint. 32 | 33 | proc cpPinJointInit*(joint: ptr cpPinJoint; a: ptr cpBody; b: ptr cpBody; anchorA: cpVect; 34 | anchorB: cpVect): ptr cpPinJoint {.importc: "cpPinJointInit", 35 | header: "".} 36 | ## / Allocate and initialize a pin joint. 37 | 38 | proc cpPinJointNew*(a: ptr cpBody; b: ptr cpBody; anchorA: cpVect; anchorB: cpVect): ptr cpConstraint {. 39 | importc: "cpPinJointNew", header: "".} 40 | ## / Get the location of the first anchor relative to the first body. 41 | 42 | proc cpPinJointGetAnchorA*(constraint: ptr cpConstraint): cpVect {. 43 | importc: "cpPinJointGetAnchorA", header: "".} 44 | ## / Set the location of the first anchor relative to the first body. 45 | 46 | proc cpPinJointSetAnchorA*(constraint: ptr cpConstraint; anchorA: cpVect) {. 47 | importc: "cpPinJointSetAnchorA", header: "".} 48 | ## / Get the location of the second anchor relative to the second body. 49 | 50 | proc cpPinJointGetAnchorB*(constraint: ptr cpConstraint): cpVect {. 51 | importc: "cpPinJointGetAnchorB", header: "".} 52 | ## / Set the location of the second anchor relative to the second body. 53 | 54 | proc cpPinJointSetAnchorB*(constraint: ptr cpConstraint; anchorB: cpVect) {. 55 | importc: "cpPinJointSetAnchorB", header: "".} 56 | ## / Get the distance the joint will maintain between the two anchors. 57 | 58 | proc cpPinJointGetDist*(constraint: ptr cpConstraint): cpFloat {. 59 | importc: "cpPinJointGetDist", header: "".} 60 | ## / Set the distance the joint will maintain between the two anchors. 61 | 62 | proc cpPinJointSetDist*(constraint: ptr cpConstraint; dist: cpFloat) {. 63 | importc: "cpPinJointSetDist", header: "".} 64 | ## /@} 65 | -------------------------------------------------------------------------------- /src/rapid/graphics/programs.nim: -------------------------------------------------------------------------------- 1 | ## Commonly used shader programs. 2 | 3 | import aglet 4 | 5 | import vertex_types 6 | 7 | const 8 | vert2DUvDirect = glsl""" 9 | #version 330 core 10 | 11 | in vec2 position; 12 | in vec2 uv; 13 | 14 | out vec2 vuv; 15 | 16 | void main(void) 17 | { 18 | gl_Position = vec4(position, 0.0, 1.0); 19 | vuv = uv; 20 | } 21 | """ 22 | 23 | vert2DColorUvDirect = glsl""" 24 | #version 330 core 25 | 26 | in vec2 position; 27 | in vec2 uv; 28 | in vec4 color; 29 | 30 | out vec2 vuv; 31 | out vec4 vcolor; 32 | 33 | void main(void) 34 | { 35 | gl_Position = vec4(position, 0.0, 1.0); 36 | vuv = uv; 37 | vcolor = color; 38 | } 39 | """ 40 | 41 | vert2DUvProjection = glsl""" 42 | #version 330 core 43 | 44 | in vec2 position; 45 | in vec2 uv; 46 | 47 | uniform mat4 projection; 48 | 49 | out vec2 vuv; 50 | 51 | void main(void) 52 | { 53 | gl_Position = projection * vec4(position, 0.0, 1.0); 54 | vuv = uv; 55 | } 56 | """ 57 | 58 | vert2DColorUvProjection = glsl""" 59 | #version 330 core 60 | 61 | in vec2 position; 62 | in vec2 uv; 63 | in vec4 color; 64 | 65 | uniform mat4 projection; 66 | 67 | out vec2 vuv; 68 | out vec4 vcolor; 69 | 70 | void main(void) 71 | { 72 | gl_Position = projection * vec4(position, 0.0, 1.0); 73 | vuv = uv; 74 | vcolor = color; 75 | } 76 | """ 77 | 78 | fragTextured = glsl""" 79 | #version 330 core 80 | 81 | in vec2 vuv; 82 | 83 | uniform sampler2D sampler; 84 | 85 | out vec4 color; 86 | 87 | void main(void) 88 | { 89 | color = texture(sampler, vuv); 90 | } 91 | """ 92 | 93 | fragColoredTextured = glsl""" 94 | #version 330 core 95 | 96 | in vec2 vuv; 97 | in vec4 vcolor; 98 | 99 | uniform sampler2D sampler; 100 | 101 | out vec4 color; 102 | 103 | void main(void) 104 | { 105 | color = texture(sampler, vuv) * vcolor; 106 | } 107 | """ 108 | 109 | proc programDirect*(window: Window, _: type Vertex2dUv): auto = 110 | ## Creates a shader program for drawing 2D graphics without any matrices, 111 | ## directly using normalized device coordinates. 112 | ## 113 | ## Uniforms 114 | ## ======== 115 | ## 116 | ## - ``sampler: Sampler2D`` – the sampler to use for texturing 117 | 118 | window.newProgram[:_]( 119 | vertexSrc = vert2DUvDirect, 120 | fragmentSrc = fragTextured, 121 | ) 122 | 123 | proc programDirect*(window: Window, _: type Vertex2dColorUv): auto = 124 | 125 | window.newProgram[:_]( 126 | vertexSrc = vert2DColorUvDirect, 127 | fragmentSrc = fragColoredTextured, 128 | ) 129 | 130 | proc programProjection*(window: Window, _: type Vertex2dUv): auto = 131 | ## Creates a shader program for drawing 2D graphics with a projection matrix 132 | ## only. 133 | ## 134 | ## Uniforms 135 | ## ======== 136 | ## 137 | ## - ``projection: Mat4`` – the projection matrix applied to vertices 138 | ## - ``sampler: Sampler2D`` – the sampler to use for texturing 139 | 140 | window.newProgram[:_]( 141 | vertexSrc = vert2DUvProjection, 142 | fragmentSrc = fragTextured, 143 | ) 144 | 145 | proc programProjection*(window: Window, _: type Vertex2dColorUv): auto = 146 | 147 | window.newProgram[:_]( 148 | vertexSrc = vert2DColorUvProjection, 149 | fragmentSrc = fragColoredTextured, 150 | ) 151 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/groove_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpGrooveJoint cpGrooveJoint 22 | ## / @{ 23 | ## / Check if a constraint is a slide joint. 24 | 25 | proc cpConstraintIsGrooveJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsGrooveJoint", header: "".} 27 | ## / Allocate a groove joint. 28 | 29 | proc cpGrooveJointAlloc*(): ptr cpGrooveJoint {.importc: "cpGrooveJointAlloc", 30 | header: "".} 31 | ## / Initialize a groove joint. 32 | 33 | proc cpGrooveJointInit*(joint: ptr cpGrooveJoint; a: ptr cpBody; b: ptr cpBody; 34 | groove_a: cpVect; groove_b: cpVect; anchorB: cpVect): ptr cpGrooveJoint {. 35 | importc: "cpGrooveJointInit", header: "".} 36 | ## / Allocate and initialize a groove joint. 37 | 38 | proc cpGrooveJointNew*(a: ptr cpBody; b: ptr cpBody; groove_a: cpVect; groove_b: cpVect; 39 | anchorB: cpVect): ptr cpConstraint {. 40 | importc: "cpGrooveJointNew", header: "".} 41 | ## / Get the first endpoint of the groove relative to the first body. 42 | 43 | proc cpGrooveJointGetGrooveA*(constraint: ptr cpConstraint): cpVect {. 44 | importc: "cpGrooveJointGetGrooveA", header: "".} 45 | ## / Set the first endpoint of the groove relative to the first body. 46 | 47 | proc cpGrooveJointSetGrooveA*(constraint: ptr cpConstraint; grooveA: cpVect) {. 48 | importc: "cpGrooveJointSetGrooveA", header: "".} 49 | ## / Get the first endpoint of the groove relative to the first body. 50 | 51 | proc cpGrooveJointGetGrooveB*(constraint: ptr cpConstraint): cpVect {. 52 | importc: "cpGrooveJointGetGrooveB", header: "".} 53 | ## / Set the first endpoint of the groove relative to the first body. 54 | 55 | proc cpGrooveJointSetGrooveB*(constraint: ptr cpConstraint; grooveB: cpVect) {. 56 | importc: "cpGrooveJointSetGrooveB", header: "".} 57 | ## / Get the location of the second anchor relative to the second body. 58 | 59 | proc cpGrooveJointGetAnchorB*(constraint: ptr cpConstraint): cpVect {. 60 | importc: "cpGrooveJointGetAnchorB", header: "".} 61 | ## / Set the location of the second anchor relative to the second body. 62 | 63 | proc cpGrooveJointSetAnchorB*(constraint: ptr cpConstraint; anchorB: cpVect) {. 64 | importc: "cpGrooveJointSetAnchorB", header: "".} 65 | ## / @} 66 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/slide_joint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpSlideJoint cpSlideJoint 22 | ## / @{ 23 | ## / Check if a constraint is a slide joint. 24 | 25 | proc cpConstraintIsSlideJoint*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsSlideJoint", header: "".} 27 | ## / Allocate a slide joint. 28 | 29 | proc cpSlideJointAlloc*(): ptr cpSlideJoint {.importc: "cpSlideJointAlloc", 30 | header: "".} 31 | ## / Initialize a slide joint. 32 | 33 | proc cpSlideJointInit*(joint: ptr cpSlideJoint; a: ptr cpBody; b: ptr cpBody; 34 | anchorA: cpVect; anchorB: cpVect; min: cpFloat; max: cpFloat): ptr cpSlideJoint {. 35 | importc: "cpSlideJointInit", header: "".} 36 | ## / Allocate and initialize a slide joint. 37 | 38 | proc cpSlideJointNew*(a: ptr cpBody; b: ptr cpBody; anchorA: cpVect; anchorB: cpVect; 39 | min: cpFloat; max: cpFloat): ptr cpConstraint {. 40 | importc: "cpSlideJointNew", header: "".} 41 | ## / Get the location of the first anchor relative to the first body. 42 | 43 | proc cpSlideJointGetAnchorA*(constraint: ptr cpConstraint): cpVect {. 44 | importc: "cpSlideJointGetAnchorA", header: "".} 45 | ## / Set the location of the first anchor relative to the first body. 46 | 47 | proc cpSlideJointSetAnchorA*(constraint: ptr cpConstraint; anchorA: cpVect) {. 48 | importc: "cpSlideJointSetAnchorA", header: "".} 49 | ## / Get the location of the second anchor relative to the second body. 50 | 51 | proc cpSlideJointGetAnchorB*(constraint: ptr cpConstraint): cpVect {. 52 | importc: "cpSlideJointGetAnchorB", header: "".} 53 | ## / Set the location of the second anchor relative to the second body. 54 | 55 | proc cpSlideJointSetAnchorB*(constraint: ptr cpConstraint; anchorB: cpVect) {. 56 | importc: "cpSlideJointSetAnchorB", header: "".} 57 | ## / Get the minimum distance the joint will maintain between the two anchors. 58 | 59 | proc cpSlideJointGetMin*(constraint: ptr cpConstraint): cpFloat {. 60 | importc: "cpSlideJointGetMin", header: "".} 61 | ## / Set the minimum distance the joint will maintain between the two anchors. 62 | 63 | proc cpSlideJointSetMin*(constraint: ptr cpConstraint; min: cpFloat) {. 64 | importc: "cpSlideJointSetMin", header: "".} 65 | ## / Get the maximum distance the joint will maintain between the two anchors. 66 | 67 | proc cpSlideJointGetMax*(constraint: ptr cpConstraint): cpFloat {. 68 | importc: "cpSlideJointGetMax", header: "".} 69 | ## / Set the maximum distance the joint will maintain between the two anchors. 70 | 71 | proc cpSlideJointSetMax*(constraint: ptr cpConstraint; max: cpFloat) {. 72 | importc: "cpSlideJointSetMax", header: "".} 73 | ## / @} 74 | -------------------------------------------------------------------------------- /src/rapid/graphics/image.nim: -------------------------------------------------------------------------------- 1 | ## Generic RGBA image buffer. 2 | 3 | import aglet/pixeltypes 4 | import aglet/rect 5 | import stb_image/read as stbi 6 | 7 | import ../math as rmath 8 | 9 | type 10 | Image* {.byref.} = object 11 | width*, height*: int32 12 | data*: seq[uint8] 13 | 14 | proc `[]`*(image: Image, position: Vec2i): Rgba8 {.inline.} = 15 | ## Returns the pixel at the given position. 16 | 17 | let 18 | redIndex = (position.x + position.y * image.width) * 4 19 | greenIndex = redIndex + 1 20 | blueIndex = greenIndex + 1 21 | alphaIndex = blueIndex + 1 22 | result = rgba8( 23 | image.data[redIndex], 24 | image.data[greenIndex], 25 | image.data[blueIndex], 26 | image.data[alphaIndex], 27 | ) 28 | 29 | proc `[]`*(image: Image, x, y: int32): Rgba8 {.inline.} = 30 | ## Shortcut for querying a pixel with a vector. 31 | image[vec2i(x, y)] 32 | 33 | proc debugRepr*(image: Image): string = 34 | ## Returns a string containing the image represented in ASCII. For debugging 35 | ## purposes only. 36 | 37 | for y in 0.. 0: result.add('\n') 39 | for x in 0..= 0 and rect.y >= 0, "rect coordinates must be inbounds" 71 | assert rect.x + rect.width <= image.width and 72 | rect.y + rect.height <= image.height, 73 | "rect must not extend beyond the image's size" 74 | 75 | result.width = rect.width 76 | result.height = rect.height 77 | result.data.setLen(rect.width * rect.height * 4) 78 | for y in rect.top.. packer.width: 83 | return int32.none 84 | 85 | var i = index 86 | while spaceLeft > 0: 87 | if i == packer.nodes.len: 88 | return int32.none 89 | position.y = max(position.y, packer.nodes[i].y) 90 | if position.y + size.y > packer.height: 91 | return int32.none 92 | spaceLeft -= packer.nodes[i].width 93 | inc(i) 94 | result = some(position.y) 95 | 96 | proc pack*(packer: var RectPacker, size: Vec2i): Option[Recti] = 97 | ## Packs a rectangle to the packer's bin and returns ``Some(rect)``. If the 98 | ## rectangle doesn't fit, returns ``None``. 99 | 100 | var 101 | bestSize = packer.size 102 | bestPosition = Vec2i.none 103 | bestIndex = int.none 104 | 105 | for index, node in packer.nodes: 106 | let maybeY = packer.rectFits(index, size) 107 | if maybeY.isSome: 108 | let y = maybeY.get 109 | if y + size.y < bestSize.y or 110 | (y + size.y == bestSize.y and node.width < bestSize.x): 111 | bestIndex = some(index) 112 | bestSize = vec2i(node.width, y + size.y) 113 | bestPosition = some(vec2i(node.x, y)) 114 | 115 | if bestIndex.isNone: 116 | return Recti.none 117 | 118 | let rect = recti(bestPosition.get, size) 119 | packer.addSkylineLevel(bestIndex.get, rect) 120 | result = some(rect) 121 | 122 | proc init*(packer: var RectPacker, size: Vec2i) = 123 | ## Initializes a rect packer. 124 | 125 | packer.size = size 126 | packer.nodes.setLen(0) 127 | packer.nodes.add(Node(width: size.x)) 128 | 129 | proc initRectPacker*(size: Vec2i): RectPacker = 130 | ## Creates and initializes a rect packer. 131 | result.init(size) 132 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/poly_shape.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpPolyShape cpPolyShape 22 | ## / @{ 23 | ## / Allocate a polygon shape. 24 | 25 | import bb, types 26 | 27 | proc cpPolyShapeAlloc*(): ptr cpPolyShape {.importc: "cpPolyShapeAlloc", 28 | header: "".} 29 | ## / Initialize a polygon shape with rounded corners. 30 | ## / A convex hull will be created from the vertexes. 31 | 32 | proc cpPolyShapeInit*(poly: ptr cpPolyShape; body: ptr cpBody; count: cint; 33 | verts: ptr cpVect; transform: cpTransform; radius: cpFloat): ptr cpPolyShape {. 34 | importc: "cpPolyShapeInit", header: "".} 35 | ## / Initialize a polygon shape with rounded corners. 36 | ## / The vertexes must be convex with a counter-clockwise winding. 37 | 38 | proc cpPolyShapeInitRaw*(poly: ptr cpPolyShape; body: ptr cpBody; count: cint; 39 | verts: ptr cpVect; radius: cpFloat): ptr cpPolyShape {. 40 | importc: "cpPolyShapeInitRaw", header: "".} 41 | ## / Allocate and initialize a polygon shape with rounded corners. 42 | ## / A convex hull will be created from the vertexes. 43 | 44 | proc cpPolyShapeNew*(body: ptr cpBody; count: cint; verts: ptr cpVect; 45 | transform: cpTransform; radius: cpFloat): ptr cpShape {. 46 | importc: "cpPolyShapeNew", header: "".} 47 | ## / Allocate and initialize a polygon shape with rounded corners. 48 | ## / The vertexes must be convex with a counter-clockwise winding. 49 | 50 | proc cpPolyShapeNewRaw*(body: ptr cpBody; count: cint; verts: ptr cpVect; radius: cpFloat): ptr cpShape {. 51 | importc: "cpPolyShapeNewRaw", header: "".} 52 | ## / Initialize a box shaped polygon shape with rounded corners. 53 | 54 | proc cpBoxShapeInit*(poly: ptr cpPolyShape; body: ptr cpBody; width: cpFloat; 55 | height: cpFloat; radius: cpFloat): ptr cpPolyShape {. 56 | importc: "cpBoxShapeInit", header: "".} 57 | ## / Initialize an offset box shaped polygon shape with rounded corners. 58 | 59 | proc cpBoxShapeInit2*(poly: ptr cpPolyShape; body: ptr cpBody; box: cpBB; radius: cpFloat): ptr cpPolyShape {. 60 | importc: "cpBoxShapeInit2", header: "".} 61 | ## / Allocate and initialize a box shaped polygon shape. 62 | 63 | proc cpBoxShapeNew*(body: ptr cpBody; width: cpFloat; height: cpFloat; radius: cpFloat): ptr cpShape {. 64 | importc: "cpBoxShapeNew", header: "".} 65 | ## / Allocate and initialize an offset box shaped polygon shape. 66 | 67 | proc cpBoxShapeNew2*(body: ptr cpBody; box: cpBB; radius: cpFloat): ptr cpShape {. 68 | importc: "cpBoxShapeNew2", header: "".} 69 | ## / Get the number of verts in a polygon shape. 70 | 71 | proc cpPolyShapeGetCount*(shape: ptr cpShape): cint {.importc: "cpPolyShapeGetCount", 72 | header: "".} 73 | ## / Get the @c ith vertex of a polygon shape. 74 | 75 | proc cpPolyShapeGetVert*(shape: ptr cpShape; index: cint): cpVect {. 76 | importc: "cpPolyShapeGetVert", header: "".} 77 | ## / Get the radius of a polygon shape. 78 | 79 | proc cpPolyShapeGetRadius*(shape: ptr cpShape): cpFloat {. 80 | importc: "cpPolyShapeGetRadius", header: "".} 81 | ## / @} 82 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/damped_rotary_spring.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpDampedRotarySpring cpDampedRotarySpring 22 | ## / @{ 23 | ## / Check if a constraint is a damped rotary springs. 24 | 25 | proc cpConstraintIsDampedRotarySpring*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsDampedRotarySpring", header: "".} 27 | ## / Function type used for damped rotary spring force callbacks. 28 | 29 | type 30 | cpDampedRotarySpringTorqueFunc* = proc (spring: ptr cpConstraint; 31 | relativeAngle: cpFloat): cpFloat 32 | 33 | ## / Allocate a damped rotary spring. 34 | 35 | proc cpDampedRotarySpringAlloc*(): ptr cpDampedRotarySpring {. 36 | importc: "cpDampedRotarySpringAlloc", header: "".} 37 | ## / Initialize a damped rotary spring. 38 | 39 | proc cpDampedRotarySpringInit*(joint: ptr cpDampedRotarySpring; a: ptr cpBody; 40 | b: ptr cpBody; restAngle: cpFloat; stiffness: cpFloat; 41 | damping: cpFloat): ptr cpDampedRotarySpring {. 42 | importc: "cpDampedRotarySpringInit", header: "".} 43 | ## / Allocate and initialize a damped rotary spring. 44 | 45 | proc cpDampedRotarySpringNew*(a: ptr cpBody; b: ptr cpBody; restAngle: cpFloat; 46 | stiffness: cpFloat; damping: cpFloat): ptr cpConstraint {. 47 | importc: "cpDampedRotarySpringNew", header: "".} 48 | ## / Get the rest length of the spring. 49 | 50 | proc cpDampedRotarySpringGetRestAngle*(constraint: ptr cpConstraint): cpFloat {. 51 | importc: "cpDampedRotarySpringGetRestAngle", header: "".} 52 | ## / Set the rest length of the spring. 53 | 54 | proc cpDampedRotarySpringSetRestAngle*(constraint: ptr cpConstraint; 55 | restAngle: cpFloat) {. 56 | importc: "cpDampedRotarySpringSetRestAngle", header: "".} 57 | ## / Get the stiffness of the spring in force/distance. 58 | 59 | proc cpDampedRotarySpringGetStiffness*(constraint: ptr cpConstraint): cpFloat {. 60 | importc: "cpDampedRotarySpringGetStiffness", header: "".} 61 | ## / Set the stiffness of the spring in force/distance. 62 | 63 | proc cpDampedRotarySpringSetStiffness*(constraint: ptr cpConstraint; 64 | stiffness: cpFloat) {. 65 | importc: "cpDampedRotarySpringSetStiffness", header: "".} 66 | ## / Get the damping of the spring. 67 | 68 | proc cpDampedRotarySpringGetDamping*(constraint: ptr cpConstraint): cpFloat {. 69 | importc: "cpDampedRotarySpringGetDamping", header: "".} 70 | ## / Set the damping of the spring. 71 | 72 | proc cpDampedRotarySpringSetDamping*(constraint: ptr cpConstraint; damping: cpFloat) {. 73 | importc: "cpDampedRotarySpringSetDamping", header: "".} 74 | ## / Get the damping of the spring. 75 | 76 | proc cpDampedRotarySpringGetSpringTorqueFunc*(constraint: ptr cpConstraint): cpDampedRotarySpringTorqueFunc {. 77 | importc: "cpDampedRotarySpringGetSpringTorqueFunc", 78 | header: "".} 79 | ## / Set the damping of the spring. 80 | 81 | proc cpDampedRotarySpringSetSpringTorqueFunc*(constraint: ptr cpConstraint; 82 | springTorqueFunc: cpDampedRotarySpringTorqueFunc) {. 83 | importc: "cpDampedRotarySpringSetSpringTorqueFunc", 84 | header: "".} 85 | ## / @} 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | A game engine written in Nim, optimized for making cool games fast. 6 | Made for convenience while coding and better performance than all-in-one 7 | solutions like Godot. 8 | 9 | **Note:** As of June 2021, I'm not longer actively working on rapid. 10 | Please fork the repository if you want to keep the project alive. 11 | 12 | ## Goals 13 | 14 | - Be easy to understand, 15 | - Have a rich set of flexible APIs, 16 | - Compile all C libraries statically to avoid dependency hell/linker errors, 17 | - Make game development a fun task for everyone. 18 | 19 | ## Features 20 | 21 | - `rapid/graphics` 22 | - _Almost stateless_ graphics context API – the only state you ever have 23 | to worry about is the shape buffer 24 | - Supports text rendering using FreeType 25 | - Has a built-in polyline renderer for drawing wires, graphs, etc. 26 | - Post-processing effects with HDR rendering support 27 | - Built in texture packer 28 | - `rapid/game` 29 | - Fixed timestep game loop 30 | - Fixed-size and infinite-size tilemaps 31 | - `rapid/ec` 32 | - Minimal [entity-component][gpp component] decoupling pattern implementation 33 | - `rapid/physics` 34 | - `chipmunk` – General-purpose physics engine, using 35 | [Chipmunk2D][chipmunk repo] 36 | - `simple` – Simple and fast AABB-based physics engine 37 | - `rapid/input` 38 | - Simplified input event distribution using procs like 39 | `mouseButtonJustPressed` + callback support 40 | - `rapid/math` 41 | - Common math utilities for vector math, axis-aligned bounding boxes, 42 | interpolation, and type-safe units 43 | - `rapid/ui` – [Fidget][fidget repo]-style UI framework for games 44 | and applications 45 | 46 | [gpp component]: https://gameprogrammingpatterns.com/component.html 47 | [chipmunk repo]: https://github.com/slembcke/Chipmunk2D 48 | [fidget repo]: https://github.com/treeform/fidget 49 | 50 | ### Coming soon 51 | 52 | - `rapid/audio` – Sound mixer with real-time effect support 53 | 54 | ## Installing 55 | 56 | Note that the new version of rapid (2020) is still under development, so you 57 | will have to install a specific commit from the master branch. The current 58 | release version is not supported anymore. 59 | 60 | To install rapid, use the following command: 61 | ```bash 62 | $ nimble install "rapid@#3e831cb" # change the commit hash to the latest commit 63 | ``` 64 | 65 | In your `.nimble` file: 66 | ```nim 67 | requires "rapid#3e831cb" 68 | ``` 69 | 70 | Pinning to a specific commit rather than `#head` is recommended, because `#head` 71 | doesn't name a specific point in development. This means that two different 72 | packages may end up requiring `#head`, and the `#head` that's installed locally 73 | may not match the `#head` that's required by the package. Thanks nimble, 74 | you're the best. 75 | 76 | ### Linux 77 | 78 | On Linux, the development headers for the following libraries must be installed: 79 | 80 | - for `rapid/graphics`: 81 | - GL 82 | - X11 83 | - Xrandr 84 | - Xxf86vm 85 | - Xi 86 | - Xcursor 87 | - Xinerama 88 | 89 | #### Debian and Ubuntu 90 | ```sh 91 | sudo apt install \ 92 | libgl-dev libx11-dev libxrandr-dev libxxf86vm-dev libxi-dev libxcursor-dev \ 93 | libxinerama-dev 94 | ``` 95 | 96 | #### Fedora 97 | ```sh 98 | sudo dnf install \ 99 | mesa-libGL-devel libX11-devel libXrandr-devel libXxf86vm-devel \ 100 | libXinerama-devel libXi-devel libXcursor-devel 101 | ``` 102 | 103 | #### openSUSE 104 | ```sh 105 | sudo zypper in \ 106 | Mesa-libGL-devel libX11-devel libXrandr-devel libXxf86vm-devel \ 107 | libXinerama-devel libXi-devel libXcursor-devel 108 | ``` 109 | 110 | ## Examples 111 | 112 | For examples, look in the `tests` directory. 113 | 114 | ## Tips 115 | 116 | - Draw in batches whenever possible. This reduces the amount of time the CPU 117 | has to spend sending draw calls to the GPU, making your game run better. 118 | In general, whenever you have some object that doesn't change often, prefer 119 | an aglet `Mesh` rather than rapid's `Graphics`. 120 | - Compile your game with `--opt:speed`. Nim's rather primitive stack trace 121 | system can slow programs down by quite a bit, so compiling with speed 122 | optimizations enabled can be quite important to maintain playable 123 | performance. Though if your game's so CPU-heavy that it becomes unplayable 124 | without `--opt:speed`, you're doing something wrong. Go fix your code. 125 | 126 | ## Contributing 127 | 128 | When contributing code, please follow the [coding style guidelines](code_style.md). 129 | 130 | ### Super Secret Messages hidden in plain sight 131 | 132 | #### A message to Araq 133 | 134 | Fast incremental compilation when 135 | 136 | -------------------------------------------------------------------------------- /tests/sampleData/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/engine.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | 22 | include compile_chipmunk 23 | 24 | proc cpMessage*(condition: cstring; file: cstring; line: cint; isError: cint; 25 | isHardError: cint; message: cstring) {.varargs, importc: "cpMessage", 26 | header: "".} 27 | 28 | import types, vect, bb 29 | export types, vect, bb 30 | 31 | include transform 32 | include spatial_index 33 | include arbiter 34 | include body 35 | include shape 36 | include poly_shape 37 | include constraint 38 | include space 39 | 40 | ## Chipmunk 7.0.3 41 | 42 | const 43 | CP_VERSION_MAJOR* = 7 44 | CP_VERSION_MINOR* = 0 45 | CP_VERSION_RELEASE* = 3 46 | 47 | ## / Version string. 48 | 49 | var cpVersionString* {.importc: "cpVersionString", header: "".}: cstring 50 | 51 | ## / Calculate the moment of inertia for a circle. 52 | ## / @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. 53 | 54 | proc cpMomentForCircle*(m: cpFloat; r1: cpFloat; r2: cpFloat; offset: cpVect): cpFloat {. 55 | importc: "cpMomentForCircle", header: "".} 56 | ## / Calculate area of a hollow circle. 57 | ## / @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. 58 | 59 | proc cpAreaForCircle*(r1: cpFloat; r2: cpFloat): cpFloat {.importc: "cpAreaForCircle", 60 | header: "".} 61 | ## / Calculate the moment of inertia for a line segment. 62 | ## / Beveling radius is not supported. 63 | 64 | proc cpMomentForSegment*(m: cpFloat; a: cpVect; b: cpVect; radius: cpFloat): cpFloat {. 65 | importc: "cpMomentForSegment", header: "".} 66 | ## / Calculate the area of a fattened (capsule shaped) line segment. 67 | 68 | proc cpAreaForSegment*(a: cpVect; b: cpVect; radius: cpFloat): cpFloat {. 69 | importc: "cpAreaForSegment", header: "".} 70 | ## / Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex. 71 | 72 | proc cpMomentForPoly*(m: cpFloat; count: cint; verts: ptr cpVect; offset: cpVect; 73 | radius: cpFloat): cpFloat {.importc: "cpMomentForPoly", 74 | header: "".} 75 | ## / Calculate the signed area of a polygon. A Clockwise winding gives positive area. 76 | ## / This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes. 77 | 78 | proc cpAreaForPoly*(count: cint; verts: ptr cpVect; radius: cpFloat): cpFloat {. 79 | importc: "cpAreaForPoly", header: "".} 80 | ## / Calculate the natural centroid of a polygon. 81 | 82 | proc cpCentroidForPoly*(count: cint; verts: ptr cpVect): cpVect {. 83 | importc: "cpCentroidForPoly", header: "".} 84 | ## / Calculate the moment of inertia for a solid box. 85 | 86 | proc cpMomentForBox*(m: cpFloat; width: cpFloat; height: cpFloat): cpFloat {. 87 | importc: "cpMomentForBox", header: "".} 88 | ## / Calculate the moment of inertia for a solid box. 89 | 90 | proc cpMomentForBox2*(m: cpFloat; box: cpBB): cpFloat {.importc: "cpMomentForBox2", 91 | header: "".} 92 | 93 | proc cpConvexHull*(count: cint; verts: ptr cpVect; result: ptr cpVect; first: ptr cint; 94 | tol: cpFloat): cint {.importc: "cpConvexHull", header: "".} 95 | 96 | proc cpClosetPointOnSegment*(p: cpVect; a: cpVect; b: cpVect): cpVect {.inline.} = 97 | var delta: cpVect 98 | var t: cpFloat 99 | return cpvadd(b, cpvmult(delta, t)) 100 | 101 | ## @} 102 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/damped_spring.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpDampedSpring cpDampedSpring 22 | ## / @{ 23 | ## / Check if a constraint is a slide joint. 24 | 25 | proc cpConstraintIsDampedSpring*(constraint: ptr cpConstraint): cpBool {. 26 | importc: "cpConstraintIsDampedSpring", header: "".} 27 | ## / Function type used for damped spring force callbacks. 28 | 29 | type 30 | cpDampedSpringForceFunc* = proc (spring: ptr cpConstraint; dist: cpFloat): cpFloat 31 | 32 | ## / Allocate a damped spring. 33 | 34 | proc cpDampedSpringAlloc*(): ptr cpDampedSpring {.importc: "cpDampedSpringAlloc", 35 | header: "".} 36 | ## / Initialize a damped spring. 37 | 38 | proc cpDampedSpringInit*(joint: ptr cpDampedSpring; a: ptr cpBody; b: ptr cpBody; 39 | anchorA: cpVect; anchorB: cpVect; restLength: cpFloat; 40 | stiffness: cpFloat; damping: cpFloat): ptr cpDampedSpring {. 41 | importc: "cpDampedSpringInit", header: "".} 42 | ## / Allocate and initialize a damped spring. 43 | 44 | proc cpDampedSpringNew*(a: ptr cpBody; b: ptr cpBody; anchorA: cpVect; anchorB: cpVect; 45 | restLength: cpFloat; stiffness: cpFloat; damping: cpFloat): ptr cpConstraint {. 46 | importc: "cpDampedSpringNew", header: "".} 47 | ## / Get the location of the first anchor relative to the first body. 48 | 49 | proc cpDampedSpringGetAnchorA*(constraint: ptr cpConstraint): cpVect {. 50 | importc: "cpDampedSpringGetAnchorA", header: "".} 51 | ## / Set the location of the first anchor relative to the first body. 52 | 53 | proc cpDampedSpringSetAnchorA*(constraint: ptr cpConstraint; anchorA: cpVect) {. 54 | importc: "cpDampedSpringSetAnchorA", header: "".} 55 | ## / Get the location of the second anchor relative to the second body. 56 | 57 | proc cpDampedSpringGetAnchorB*(constraint: ptr cpConstraint): cpVect {. 58 | importc: "cpDampedSpringGetAnchorB", header: "".} 59 | ## / Set the location of the second anchor relative to the second body. 60 | 61 | proc cpDampedSpringSetAnchorB*(constraint: ptr cpConstraint; anchorB: cpVect) {. 62 | importc: "cpDampedSpringSetAnchorB", header: "".} 63 | ## / Get the rest length of the spring. 64 | 65 | proc cpDampedSpringGetRestLength*(constraint: ptr cpConstraint): cpFloat {. 66 | importc: "cpDampedSpringGetRestLength", header: "".} 67 | ## / Set the rest length of the spring. 68 | 69 | proc cpDampedSpringSetRestLength*(constraint: ptr cpConstraint; restLength: cpFloat) {. 70 | importc: "cpDampedSpringSetRestLength", header: "".} 71 | ## / Get the stiffness of the spring in force/distance. 72 | 73 | proc cpDampedSpringGetStiffness*(constraint: ptr cpConstraint): cpFloat {. 74 | importc: "cpDampedSpringGetStiffness", header: "".} 75 | ## / Set the stiffness of the spring in force/distance. 76 | 77 | proc cpDampedSpringSetStiffness*(constraint: ptr cpConstraint; stiffness: cpFloat) {. 78 | importc: "cpDampedSpringSetStiffness", header: "".} 79 | ## / Get the damping of the spring. 80 | 81 | proc cpDampedSpringGetDamping*(constraint: ptr cpConstraint): cpFloat {. 82 | importc: "cpDampedSpringGetDamping", header: "".} 83 | ## / Set the damping of the spring. 84 | 85 | proc cpDampedSpringSetDamping*(constraint: ptr cpConstraint; damping: cpFloat) {. 86 | importc: "cpDampedSpringSetDamping", header: "".} 87 | ## / Get the damping of the spring. 88 | 89 | proc cpDampedSpringGetSpringForceFunc*(constraint: ptr cpConstraint): cpDampedSpringForceFunc {. 90 | importc: "cpDampedSpringGetSpringForceFunc", header: "".} 91 | ## / Set the damping of the spring. 92 | 93 | proc cpDampedSpringSetSpringForceFunc*(constraint: ptr cpConstraint; 94 | springForceFunc: cpDampedSpringForceFunc) {. 95 | importc: "cpDampedSpringSetSpringForceFunc", header: "".} 96 | ## / @} 97 | -------------------------------------------------------------------------------- /tests/tchipmunk.nim: -------------------------------------------------------------------------------- 1 | # test for chipmunk physics wrapper 2 | 3 | import aglet 4 | import aglet/window/glfw 5 | import rapid/game 6 | import rapid/graphics 7 | import rapid/graphics/postprocess 8 | import rapid/math/interpolation 9 | {.define: rapidChipmunkGraphicsDebugDraw.} 10 | import rapid/physics/chipmunk 11 | 12 | type 13 | Droplet = object 14 | angle: Radians 15 | 16 | const 17 | ParticleSize = 4 18 | 19 | proc spawnBox(space: Space, position, velocity: Vec2f) = 20 | 21 | var 22 | body = newDynamicBody(Droplet()).addTo(space) 23 | shape = body.newCircleShape(radius = ParticleSize) 24 | body.position = position 25 | body.velocity = velocity 26 | shape.density = 100 27 | shape.elasticity = 0.5 28 | # shape.friction = 0.4 29 | 30 | space.reindexShapesForBody(body) 31 | 32 | proc main() = 33 | 34 | var agl = initAglet() 35 | agl.initWindow() 36 | 37 | var 38 | window = agl.newWindowGlfw(400, 400, "tchipmunk", 39 | winHints(resizable = false, 40 | msaaSamples = 8)) 41 | graphics = window.newGraphics() 42 | effectBuffer = window.newEffectBuffer(window.size) 43 | space = newSpace(gravity = vec2f(0, 60 * 8), iterations = 10) 44 | placing = false 45 | debugDraw = false 46 | lastMousePosition = vec2f(0, 0) 47 | 48 | var 49 | floor = newStaticBody().addTo(space) 50 | floorSegments = [ 51 | (a: vec2f(68, 85), b: vec2f(200, 161)), 52 | (a: vec2f(149, 306), b: vec2f(330, 200)), 53 | (a: vec2f(34, 364), b: vec2f(234, 364)), 54 | ] 55 | for (a, b) in floorSegments: 56 | var segment = floor.newSegmentShape(a, b, radius = 2) 57 | segment.elasticity = 0.5 58 | # segment.friction = 0.4 59 | 60 | let 61 | blobSprite = block: 62 | const blobSize = 16 63 | var pixels: seq[Rgba8] 64 | for y in 0.. 500: 115 | oob.add(body) 116 | for body in oob: 117 | space.delBody(body) 118 | 119 | draw step: 120 | var frame = window.render() 121 | frame.clearColor(colWhite) 122 | 123 | var effect = effectBuffer.render() 124 | effect.clearColor(rgba(0, 0, 0, 0)) 125 | 126 | graphics.resetShape() 127 | space.eachBody do (body: Body): 128 | if body of UserBody[Droplet]: 129 | var ubody = UserBody[Droplet](body) 130 | graphics.transform: 131 | const rs = vec2f(ParticleSize * 10) 132 | let speed = max(1, body.velocity.length / 180) 133 | graphics.translate(body.position) 134 | graphics.rotate(body.velocity.angle) 135 | graphics.scale(speed, 1) 136 | echo body.position 137 | graphics.sprite(blobSprite, -rs / 2, size = rs, 138 | tint = colDarkTurquoise) 139 | graphics.draw(effect) 140 | effectBuffer.apply(alphaThreshold, uniforms { 141 | threshold: 0.15f, 142 | smoothness: 0.02f, 143 | color: colDarkTurquoise.rgba32f.Vec4f, 144 | }) 145 | effectBuffer.drawTo(frame) 146 | 147 | graphics.resetShape() 148 | for (a, b) in floorSegments: 149 | graphics.line(a, b, thickness = 2, colorA = colBlack, colorB = colBlack) 150 | graphics.draw(frame) 151 | 152 | if debugDraw: 153 | graphics.resetShape() 154 | space.debugDraw(graphics.debugDrawOptions()) 155 | graphics.draw(frame) 156 | 157 | frame.finish() 158 | 159 | when isMainModule: main() 160 | -------------------------------------------------------------------------------- /src/rapid/wrappers/freetype.nim: -------------------------------------------------------------------------------- 1 | ## Minimal, lightweight freetype wrapper. 2 | 3 | import std/macros 4 | import std/os 5 | 6 | const 7 | Here = currentSourcePath.splitPath().head 8 | # using concatenation because of windows®®®®®®®® crosscompilation 9 | Freetype = Here & "/extern/freetype" 10 | Include = Freetype & "/include" 11 | Src = Freetype & "/src" 12 | {.passC: "-I" & Include.} 13 | {.passC: "-DFT2_BUILD_LIBRARY".} 14 | 15 | macro genCompiles: untyped = 16 | const 17 | CompileList = [ 18 | "autofit/autofit.c", 19 | "base/ftsystem.c", 20 | "base/ftinit.c", 21 | "base/ftdebug.c", 22 | "base/ftbase.c", 23 | "base/ftbbox.c", 24 | "base/ftglyph.c", 25 | "base/ftbdf.c", 26 | "base/ftbitmap.c", 27 | "bdf/bdf.c", 28 | "cff/cff.c", 29 | "pshinter/pshinter.c", 30 | "psnames/psnames.c", 31 | "sfnt/sfnt.c", 32 | "smooth/smooth.c", 33 | "truetype/truetype.c", 34 | ] 35 | var pragmas = newNimNode(nnkPragma) 36 | for file in CompileList: 37 | pragmas.add(newColonExpr(ident"compile", newLit(Src & "/" & file))) 38 | result = newStmtList(pragmas) 39 | genCompiles 40 | 41 | type 42 | FtPos* = clong 43 | FtFixed* = clong 44 | Ft26dot6* = clong 45 | FtVector* = object 46 | x*, y*: FtPos 47 | FtMatrix* = object 48 | xx*, xy*, yx*, yy*: FtFixed 49 | FtBbox* = object 50 | xMin*, yMin*, xMax*, yMax*: FtPos 51 | FtBitmap* = object 52 | rows*, width*: cuint 53 | pitch*: cint 54 | buffer*: ptr UncheckedArray[uint8] 55 | num_grays*: cushort 56 | pixel_mode*, palette_mode*: cuchar 57 | palette*: pointer 58 | FtBitmapSize* = object 59 | height*, width*: cshort 60 | size*, x_ppem*, y_ppem*: FtPos 61 | FtGenericFinalizer = proc (obj: pointer) {.cdecl.} 62 | FtGeneric* = object 63 | data*: pointer 64 | generic_finalizer*: FtGenericFinalizer 65 | 66 | FtLibrary* = pointer 67 | 68 | FtSizeMetrics* = object 69 | x_ppem*, y_ppem*: cushort 70 | x_scale*, y_scale*: FtFixed 71 | ascender*, descender*, height*, max_advance*: FtPos 72 | FtSize* = ptr object 73 | face*: FtFace 74 | generic*: FtGeneric 75 | metrics*: FtSizeMetrics 76 | internal*: pointer 77 | FtFace* = ptr object 78 | num_faces*, face_index*: clong 79 | face_flags*, style_flags*: clong 80 | num_glyphs*: clong 81 | family_name*, style_name*: cstring 82 | num_fixed_sizes*: cint 83 | available_sizes*: ptr FtBitmapSize 84 | num_charmaps: cint 85 | charmaps: pointer 86 | generic*: FtGeneric 87 | bbox*: FtBbox 88 | units_per_EM*: cushort 89 | ascender*, descender*, height*: cshort 90 | max_advance_width*, max_advance_height*: cshort 91 | underline_position*, underline_thickness*: cshort 92 | glyph*: FtGlyphSlot 93 | size*: FtSize 94 | # truncated; doesn't matter because we always deal with pointers to this 95 | 96 | FtGlyphMetrics* = object 97 | width*, height*: FtPos 98 | horiBearingX*, horiBearingY*, horiAdvance*: FtPos 99 | vertBearingX*, vertBearingY*, vertAdvance*: FtPos 100 | FtGlyphFormat* = distinct cuint 101 | FtGlyphSlot* = ptr object 102 | library*: FtLibrary 103 | face*: FtFace 104 | next*: FtGlyphSlot 105 | glyph_index*: cuint 106 | generic*: FtGeneric 107 | metrics*: FtGlyphMetrics 108 | linearHoriAdvance*, linearVertAdvance*: FtFixed 109 | advance*: FtVector 110 | format*: FtGlyphFormat 111 | bitmap*: FtBitmap 112 | bitmap_left*: cint 113 | bitmap_top*: cint 114 | # truncated; doesn't matter because we always deal with pointers to this 115 | 116 | FtError* {.size: 4.} = enum 117 | # this contains fteOk only mostly because error messages are retrieved via 118 | # FT_Error_String 119 | fteOk = 0x00 120 | 121 | const 122 | ftFaceFlagScalable* = (1 shl 0) 123 | ftFaceFlagFixedSizes* = (1 shl 1) 124 | ftFaceFlagFixedWidth* = (1 shl 2) 125 | ftFaceFlagHorizontal* = (1 shl 4) 126 | ftFaceFlagVertical* = (1 shl 5) 127 | ftFaceFlagKerning* = (1 shl 6) 128 | ftLoadDefault* = 0x0 129 | ftLoadNoHinting* = (1 shl 1) 130 | ftLoadRender* = (1 shl 2) 131 | 132 | {.push cdecl.} 133 | 134 | proc initFreetype*(alibrary: var FtLibrary): FtError {.importc: "FT_Init_FreeType".} 135 | proc destroy*(library: FtLibrary): FtError {.importc: "FT_Done_FreeType".} 136 | 137 | proc newMemoryFace*(library: FtLibrary, file_base: pointer, file_size: clong, face_index: clong, aface: var FtFace): FtError {.importc: "FT_New_Memory_Face".} 138 | proc destroy*(face: FtFace): FtError {.importc: "FT_Done_Face".} 139 | proc setCharSize*(face: FtFace, char_width, char_height: Ft26dot6, horz_resolution, vert_resolution: cuint): FtError {.importc: "FT_Set_Char_Size".} 140 | proc loadChar*(face: FtFace, char_code: culong, load_flags: int32): FtError {.importc: "FT_Load_Char".} 141 | proc setTransform*(face: FtFace, matrix: ptr FtMatrix, delta: ptr FtVector) {.importc: "FT_Set_Transform".} 142 | proc getKerning*(face: FtFace, left_glyph, right_glyph, kern_mode: cuint, akerning: var FtVector): FtError {.importc: "FT_Get_Kerning".} 143 | proc getCharIndex*(face: FtFace, charcode: culong): cuint {.importc: "FT_Get_Char_Index".} 144 | 145 | proc errorString(error: FtError): cstring {.importc: "FT_Error_String".} 146 | proc `$`*(error: FtError): string = $error.errorString 147 | 148 | {.pop.} 149 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/types.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | 22 | when defined(nimHasUsed): 23 | {.used.} 24 | 25 | when sizeof(pointer) == 8: 26 | type 27 | uintptr_t* = culong 28 | else: 29 | type 30 | uintptr_t* = cuint 31 | 32 | const 33 | CP_USE_DOUBLES* = defined(rapidChipmunkUseFloat64) 34 | ## / @defgroup basicTypes Basic Types 35 | ## / Most of these types can be configured at compile time. 36 | ## / @{ 37 | 38 | when CP_USE_DOUBLES: 39 | ## / Chipmunk's floating point type. 40 | ## / Can be reconfigured at compile time. 41 | type 42 | cpFloat* = cdouble 43 | else: 44 | type 45 | cpFloat* = cfloat 46 | 47 | ## / Return the max of two cpFloats. 48 | 49 | proc cpfmax*(a: cpFloat; b: cpFloat): cpFloat {.inline.} = 50 | return if (a > b): a else: b 51 | 52 | ## / Return the min of two cpFloats. 53 | 54 | proc cpfmin*(a: cpFloat; b: cpFloat): cpFloat {.inline.} = 55 | return if (a < b): a else: b 56 | 57 | ## / Return the absolute value of a cpFloat. 58 | 59 | proc cpfabs*(f: cpFloat): cpFloat {.inline.} = 60 | return if (f < 0): -f else: f 61 | 62 | ## / Clamp @c f to be between @c min and @c max. 63 | 64 | proc cpfclamp*(f: cpFloat; min: cpFloat; max: cpFloat): cpFloat {.inline.} = 65 | return cpfmin(cpfmax(f, min), max) 66 | 67 | ## / Clamp @c f to be between 0 and 1. 68 | 69 | proc cpfclamp01*(f: cpFloat): cpFloat {.inline.} = 70 | return cpfmax(0.0, cpfmin(f, 1.0)) 71 | 72 | ## / Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent. 73 | 74 | proc cpflerp*(f1: cpFloat; f2: cpFloat; t: cpFloat): cpFloat {.inline.} = 75 | return f1 * (1.0 - t) + f2 * t 76 | 77 | ## / Linearly interpolate from @c f1 to @c f2 by no more than @c d. 78 | 79 | proc cpflerpconst*(f1: cpFloat; f2: cpFloat; d: cpFloat): cpFloat {.inline.} = 80 | return f1 + cpfclamp(f2 - f1, -d, d) 81 | 82 | ## / Hash value type. 83 | 84 | type 85 | cpHashValue* = uintptr_t 86 | ## / Type used internally to cache colliding object info for cpCollideShapes(). 87 | ## / Should be at least 32 bits. 88 | 89 | type 90 | cpCollisionID* = uint32 91 | 92 | ## Oh C, how we love to define our own boolean types to get compiler compatibility 93 | ## / Chipmunk's boolean type. 94 | 95 | type 96 | cpBool* = cuchar 97 | ## / Type used for user data pointers. 98 | type 99 | cpDataPointer* = pointer 100 | ## / Type used for cpSpace.collision_type. 101 | type 102 | cpCollisionType* = uint16 103 | ## / Type used for cpShape.group. 104 | type 105 | cpGroup* = uintptr_t 106 | ## / Type used for cpShapeFilter category and mask. 107 | type 108 | cpBitmask* = uint64 109 | ## / Type used for various timestamps in Chipmunk. 110 | type 111 | cpTimestamp* = cuint 112 | 113 | {.pragma: cpstruct, importc, header: "".} 114 | 115 | ## / Chipmunk's 2D vector type. 116 | ## / @addtogroup cpVect 117 | type 118 | cpVect* {.cpstruct.} = object 119 | x*: cpFloat 120 | y*: cpFloat 121 | 122 | ## / Column major affine transform. 123 | type 124 | cpTransform* {.cpstruct.} = object 125 | a*: cpFloat 126 | b*: cpFloat 127 | c*: cpFloat 128 | d*: cpFloat 129 | tx*: cpFloat 130 | ty*: cpFloat 131 | 132 | ## NUKE 133 | 134 | type 135 | cpMat2x2* {.cpstruct.} = object 136 | a*: cpFloat ## Row major [[a, b][c d]] 137 | b*: cpFloat 138 | c*: cpFloat 139 | d*: cpFloat 140 | 141 | {.pragma: cpistruct, importc, incompleteStruct, header: "".} 142 | 143 | type 144 | cpShape* {.cpistruct.} = object 145 | cpCircleShape* {.cpistruct.} = object 146 | cpSegmentShape* {.cpistruct.} = object 147 | cpPolyShape* {.cpistruct.} = object 148 | cpArbiter* {.cpistruct.} = object 149 | cpSpace* {.cpistruct.} = object 150 | cpBody* {.cpistruct.} = object 151 | cpConstraint* {.cpistruct.} = object 152 | cpPinJoint* {.cpistruct.} = object 153 | cpSlideJoint* {.cpistruct.} = object 154 | cpPivotJoint* {.cpistruct.} = object 155 | cpGrooveJoint* {.cpistruct.} = object 156 | cpDampedSpring* {.cpistruct.} = object 157 | cpDampedRotarySpring* {.cpistruct.} = object 158 | cpRotaryLimitJoint* {.cpistruct.} = object 159 | cpRatchetJoint* {.cpistruct.} = object 160 | cpGearJoint* {.cpistruct.} = object 161 | cpSimpleMotorJoint* {.cpistruct.} = object 162 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/bb.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | 22 | import std/math 23 | 24 | import types 25 | import vect 26 | 27 | ## / @defgroup cpBBB cpBB 28 | ## / Chipmunk's axis-aligned 2D bounding box type along with a few handy routines. 29 | ## / @{ 30 | ## / Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top) 31 | 32 | type 33 | cpBB* {.bycopy.} = object 34 | l*: cpFloat 35 | b*: cpFloat 36 | r*: cpFloat 37 | t*: cpFloat 38 | 39 | 40 | ## / Convenience constructor for cpBB structs. 41 | 42 | proc cpBBNew*(l: cpFloat; b: cpFloat; r: cpFloat; t: cpFloat): cpBB {.inline.} = 43 | var bb: cpBB = cpBB(l: l, b: b, r: r, t: t) 44 | return bb 45 | 46 | ## / Constructs a cpBB centered on a point with the given extents (half sizes). 47 | 48 | proc cpBBNewForExtents*(c: cpVect; hw: cpFloat; hh: cpFloat): cpBB {.inline.} = 49 | return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh) 50 | 51 | ## / Constructs a cpBB for a circle with the given position and radius. 52 | 53 | proc cpBBNewForCircle*(p: cpVect; r: cpFloat): cpBB {.inline.} = 54 | return cpBBNewForExtents(p, r, r) 55 | 56 | ## / Returns true if @c a and @c b intersect. 57 | 58 | proc cpBBIntersects*(a: cpBB; b: cpBB): bool {.inline.} = 59 | return a.l <= b.r and b.l <= a.r and a.b <= b.t and b.b <= a.t 60 | 61 | ## / Returns true if @c other lies completely within @c bb. 62 | 63 | proc cpBBContainsBB*(bb: cpBB; other: cpBB): bool {.inline.} = 64 | return bb.l <= other.l and bb.r >= other.r and bb.b <= other.b and bb.t >= other.t 65 | 66 | ## / Returns true if @c bb contains @c v. 67 | 68 | proc cpBBContainsVect*(bb: cpBB; v: cpVect): bool {.inline.} = 69 | return bb.l <= v.x and bb.r >= v.x and bb.b <= v.y and bb.t >= v.y 70 | 71 | ## / Returns a bounding box that holds both bounding boxes. 72 | 73 | proc cpBBMerge*(a: cpBB; b: cpBB): cpBB {.inline.} = 74 | return cpBBNew(cpfmin(a.l, b.l), cpfmin(a.b, b.b), cpfmax(a.r, b.r), cpfmax(a.t, b.t)) 75 | 76 | ## / Returns a bounding box that holds both @c bb and @c v. 77 | 78 | proc cpBBExpand*(bb: cpBB; v: cpVect): cpBB {.inline.} = 79 | return cpBBNew(cpfmin(bb.l, v.x), cpfmin(bb.b, v.y), cpfmax(bb.r, v.x), 80 | cpfmax(bb.t, v.y)) 81 | 82 | ## / Returns the center of a bounding box. 83 | 84 | proc cpBBCenter*(bb: cpBB): cpVect {.inline.} = 85 | return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5) 86 | 87 | ## / Returns the area of the bounding box. 88 | 89 | proc cpBBArea*(bb: cpBB): cpFloat {.inline.} = 90 | return (bb.r - bb.l) * (bb.t - bb.b) 91 | 92 | ## / Merges @c a and @c b and returns the area of the merged bounding box. 93 | 94 | proc cpBBMergedArea*(a: cpBB; b: cpBB): cpFloat {.inline.} = 95 | return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l)) * 96 | (cpfmax(a.t, b.t) - cpfmin(a.b, b.b)) 97 | 98 | ## / Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit. 99 | 100 | proc cpBBSegmentQuery*(bb: cpBB; a: cpVect; b: cpVect): cpFloat {.inline.} = 101 | var delta: cpVect = cpvsub(b, a) 102 | var 103 | tmin: cpFloat = -Inf 104 | tmax: cpFloat = Inf 105 | if delta.x == 0.0: 106 | if a.x < bb.l or bb.r < a.x: 107 | return Inf 108 | else: 109 | var t1: cpFloat = (bb.l - a.x) / delta.x 110 | var t2: cpFloat = (bb.r - a.x) / delta.x 111 | tmin = cpfmax(tmin, cpfmin(t1, t2)) 112 | tmax = cpfmin(tmax, cpfmax(t1, t2)) 113 | if delta.y == 0.0: 114 | if a.y < bb.b or bb.t < a.y: 115 | return Inf 116 | else: 117 | var t1: cpFloat = (bb.b - a.y) / delta.y 118 | var t2: cpFloat = (bb.t - a.y) / delta.y 119 | tmin = cpfmax(tmin, cpfmin(t1, t2)) 120 | tmax = cpfmin(tmax, cpfmax(t1, t2)) 121 | if tmin <= tmax and 0.0 <= tmax and tmin <= 1.0: 122 | return cpfmax(tmin, 0.0) 123 | else: 124 | return Inf 125 | 126 | ## / Return true if the bounding box intersects the line segment with ends @c a and @c b. 127 | 128 | proc cpBBIntersectsSegment*(bb: cpBB; a: cpVect; b: cpVect): bool {.inline.} = 129 | return cpBBSegmentQuery(bb, a, b) != Inf 130 | 131 | ## / Clamp a vector to a bounding box. 132 | 133 | proc cpBBClampVect*(bb: cpBB; v: cpVect): cpVect {.inline.} = 134 | return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t)) 135 | 136 | ## / Wrap a vector to a bounding box. 137 | 138 | proc cpBBWrapVect*(bb: cpBB; v: cpVect): cpVect {.inline.} = 139 | var dx: cpFloat = cpfabs(bb.r - bb.l) 140 | var modx: cpFloat = floorMod(v.x - bb.l, dx) 141 | var x: cpFloat = if (modx > 0.0): modx else: modx + dx 142 | var dy: cpFloat = cpfabs(bb.t - bb.b) 143 | var mody: cpFloat = floorMod(v.y - bb.b, dy) 144 | var y: cpFloat = if (mody > 0.0): mody else: mody + dy 145 | return cpv(x + bb.l, y + bb.b) 146 | 147 | ## / Returns a bounding box offseted by @c v. 148 | 149 | proc cpBBOffset*(bb: cpBB; v: cpVect): cpBB {.inline.} = 150 | return cpBBNew(bb.l + v.x, bb.b + v.y, bb.r + v.x, bb.t + v.y) 151 | 152 | ## /@} 153 | -------------------------------------------------------------------------------- /tests/tlaser.nim: -------------------------------------------------------------------------------- 1 | ## rapid/graphics - laser test 2 | ## this tests multi-target HDR post-processing effects. 3 | 4 | import std/monotimes 5 | import std/times 6 | 7 | import aglet 8 | import aglet/window/glfw 9 | import rapid/graphics 10 | import rapid/graphics/postprocess 11 | 12 | const 13 | ThresholdSplitSource = glsl""" 14 | #version 330 core 15 | 16 | in vec2 bufferUv; 17 | in vec2 pixelPosition; 18 | 19 | uniform sampler2D buffer0; 20 | uniform sampler2D buffer1; // unused 21 | uniform float threshold; 22 | 23 | layout (location = 0) out vec4 original; 24 | layout (location = 1) out vec4 crossed; 25 | 26 | float luma(vec4 color) { 27 | return (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b) * color.a; 28 | } 29 | 30 | void main() { 31 | vec4 texel = texture(buffer0, bufferUv); 32 | original = texel; 33 | 34 | float luminosity = luma(texel); 35 | vec4 crossedColor = texel * step(threshold, luminosity); 36 | crossedColor.a = luminosity - threshold; 37 | crossed = crossedColor; 38 | } 39 | """ 40 | BlurCrossedSource = glsl""" 41 | #version 330 core 42 | 43 | in vec2 bufferUv; 44 | in vec2 pixelPosition; 45 | 46 | uniform sampler2D buffer0; // original pixels 47 | uniform sampler2D buffer1; // pixels that crossed the threshold 48 | uniform vec2 bufferSize; 49 | uniform vec2 blurDirection; 50 | uniform int blurSamples; 51 | 52 | layout (location = 0) out vec4 original; 53 | layout (location = 1) out vec4 blurred; 54 | 55 | vec4 texturePixel(sampler2D tex, vec2 pix) { 56 | pix /= bufferSize; 57 | pix.y = 1.0 - pix.y; 58 | return texture(tex, pix); 59 | } 60 | 61 | float gaussian(float x) { 62 | return pow(2.71828, -(x*x / 0.125)); 63 | } 64 | 65 | void main() { 66 | original = texture(buffer0, bufferUv); 67 | 68 | float start = -floor(float(blurSamples) / 2.0); 69 | float end = start + float(blurSamples); 70 | vec4 sum = vec4(0.0); 71 | float den = 0.0; 72 | for (float i = start; i <= end; ++i) { 73 | vec2 offset = blurDirection * i; 74 | float factor = gaussian(i / -start); 75 | sum += texturePixel(buffer1, pixelPosition + offset) * factor; 76 | den += factor; 77 | } 78 | sum /= den; 79 | blurred = sum; 80 | } 81 | """ 82 | SumBuffersSource = glsl""" 83 | #version 330 core 84 | 85 | in vec2 bufferUv; 86 | in vec2 pixelPosition; 87 | 88 | uniform sampler2D buffer0; 89 | uniform sampler2D buffer1; 90 | 91 | out vec4 summed; 92 | 93 | void main() { 94 | vec4 bloom = texture(buffer1, bufferUv); 95 | summed = texture(buffer0, bufferUv) + bloom * bloom.a; 96 | } 97 | """ 98 | 99 | proc laser(graphics: Graphics, a, b: Vec2f, glowColor, coreColor: Rgba32f) = 100 | graphics.line(a, b, 32, lcRound, glowColor, glowColor) 101 | graphics.line(a, b, 16, lcRound, coreColor, coreColor) 102 | 103 | var 104 | fxThresholdSplit: PostProcess 105 | fxBlurCrossed: PostProcess 106 | fxSumBuffers: PostProcess 107 | 108 | proc bloom(buffer: EffectBuffer, 109 | threshold: float32 = 1.0, 110 | size = 32.Natural) = 111 | buffer.apply(fxThresholdSplit, uniforms { 112 | threshold: threshold, 113 | }) 114 | buffer.apply(fxBlurCrossed, uniforms { 115 | blurDirection: vec2f(1.0, 0.0), 116 | blurSamples: size.int32, 117 | }) 118 | buffer.apply(fxBlurCrossed, uniforms { 119 | blurDirection: vec2f(0.0, 1.0), 120 | blurSamples: size.int32, 121 | }) 122 | buffer.apply(fxSumBuffers, NoUniforms) 123 | 124 | proc main() = 125 | 126 | const 127 | GlowColor1 = rgba(0.0, 1.0, 0.88, 5.0) 128 | GlowColor2 = rgba(1.0, 0.0, 0.25, 5.0) 129 | CoreColor = rgba(1.0, 1.0, 1.0, 1.0) 130 | 131 | var agl = initAglet() 132 | agl.initWindow() 133 | 134 | let 135 | window = agl.newWindowGlfw(800, 600, "laser", winHints()) 136 | graphics = window.newGraphics() 137 | effects = window.newEffectBuffer(window.framebufferSize, 138 | colorTargets = 2, hdr = on) 139 | dpAdditive = defaultDrawParams().derive: 140 | blend blendAdditive 141 | 142 | var bloomEnabled = true 143 | 144 | fxThresholdSplit = window.newPostProcess(ThresholdSplitSource) 145 | fxBlurCrossed = window.newPostProcess(BlurCrossedSource) 146 | fxSumBuffers = window.newPostProcess(SumBuffersSource) 147 | 148 | window.swapInterval = 0 149 | 150 | var 151 | lastTime = getMonoTime() 152 | timeAccum = 0.0 153 | framesSinceLastReport = 0 154 | const ReportInterval = 500 155 | while not window.closeRequested: 156 | let 157 | currentTime = getMonoTime() 158 | deltaTime = currentTime - lastTime 159 | deltaMillis = deltaTime.inNanoseconds.int / 1_000_000 160 | lastTime = currentTime 161 | timeAccum += deltaMillis 162 | framesSinceLastReport.inc() 163 | if framesSinceLastReport > ReportInterval: 164 | echo timeAccum / ReportInterval, " ms (", 165 | ReportInterval / (timeAccum / 1000), " fps)" 166 | framesSinceLastReport = 0 167 | timeAccum = 0 168 | 169 | var frame = window.render() 170 | frame.clearColor(rgba(0, 0, 0, 255)) 171 | 172 | var target = effects.render() 173 | target.clearColor(rgba(0, 0, 0, 0)) 174 | graphics.transform: 175 | graphics.translate(window.size.vec2f / 2) 176 | graphics.resetShape() 177 | graphics.laser(a = vec2f(0), b = window.size.vec2f / 2 - window.mouse, 178 | GlowColor1, CoreColor) 179 | graphics.laser(a = vec2f(0), b = window.mouse - window.size.vec2f / 2, 180 | GlowColor2, CoreColor) 181 | graphics.draw(target, dpAdditive) 182 | if bloomEnabled: effects.bloom() 183 | effects.drawTo(frame) 184 | 185 | frame.finish() 186 | 187 | window.pollEvents do (event: InputEvent): 188 | case event.kind 189 | of iekWindowFrameResize: 190 | echo "resize to ", event.size 191 | effects.resize(event.size) 192 | echo effects.size 193 | of iekKeyPress: 194 | if event.key == keyB: 195 | bloomEnabled = not bloomEnabled 196 | else: discard 197 | 198 | when isMainModule: main() 199 | -------------------------------------------------------------------------------- /src/rapid/graphics/tracers.nim: -------------------------------------------------------------------------------- 1 | ## Global "tracers" for debugging purposes. 2 | 3 | import aglet/pixeltypes 4 | 5 | import ../math/vector 6 | import context 7 | 8 | type 9 | TracerTag* = enum 10 | # symbols would be nicer for this but i was unable to create a decent 11 | # implementration 12 | ttPhysics 13 | ttCollision 14 | ttOther 15 | 16 | TracerKind = enum 17 | trkPoint 18 | trkLine 19 | trkRectangle 20 | trkCircle 21 | trkText 22 | 23 | Tracer = object 24 | tags: set[TracerTag] 25 | color: Rgba32f 26 | thickness: float32 27 | case kind: TracerKind 28 | of trkPoint: 29 | point: Vec2f 30 | of trkLine: 31 | lineA, lineB: Vec2f 32 | of trkRectangle: 33 | rectangle: Rectf 34 | of trkCircle: 35 | circleCenter: Vec2f 36 | circleRadius: float32 37 | of trkText: 38 | textPosition: Vec2f 39 | text: string 40 | textAlignment: tuple[h: HorzTextAlign, v: VertTextAlign] 41 | textAlignBox: Vec2f 42 | 43 | const allTracers* = {TracerTag.low..TracerTag.high} 44 | 45 | when not defined(rapidEnableTracers) or defined(nimdoc): 46 | 47 | {.push inline.} 48 | 49 | proc resetTracers*() = 50 | ## Clears the list of tracers. 51 | ## This should be called in the update block of your game loop. 52 | 53 | proc tracePoint*(tags: set[TracerTag], point: Vec2f, 54 | size: float32 = 1, color: Rgba32f = colRed) = 55 | ## Adds a point tracer with the given tags. 56 | 57 | proc traceLine*(tags: set[TracerTag], a, b: Vec2f, 58 | thickness: float32 = 1, color: Rgba32f = colRed) = 59 | ## Adds a line tracer with the given tags. 60 | 61 | proc traceRectangle*(tags: set[TracerTag], rectangle: Rectf, 62 | thickness: float32 = 1, color: Rgba32f = colRed) = 63 | ## Adds a rectangle tracer with the given tags. 64 | 65 | proc traceCircle*(tags: set[TracerTag], center: Vec2f, radius: float32, 66 | thickness: float32 = 1, color: Rgba32f = colRed) = 67 | ## Adds a circle tracer with the given tags. 68 | 69 | proc traceText*(tags: set[TracerTag], position: Vec2f, text: string, 70 | horzAlignment = taLeft, vertAlignment = taTop, 71 | alignBox = vec2f(0), color = colWhite) = 72 | ## Adds a text tracer with the given tags. 73 | 74 | {.pop.} 75 | 76 | proc tracers*(graphics: Graphics, tags: set[TracerTag], 77 | textFont: Font = nil) = 78 | ## Draws tracers with the given tags onto the given graphics context. 79 | ## If ``textFont`` is not nil, text will be drawn with the given font. 80 | ## Otherwise no text is drawn. 81 | 82 | else: 83 | 84 | var 85 | gTracers: array[2048, Tracer] 86 | gTracerCount: uint32 87 | 88 | {.push inline.} 89 | 90 | proc resetTracers*() = 91 | gTracerCount = 0 92 | 93 | proc tracePoint*(tags: set[TracerTag], point: Vec2f, 94 | size: float32 = 1, color: Rgba32f = colRed) = 95 | 96 | gTracers[gTracerCount] = Tracer(tags: tags, color: color, 97 | thickness: size, 98 | kind: trkPoint, 99 | point: point) 100 | inc(gTracerCount) 101 | 102 | proc traceLine*(tags: set[TracerTag], a, b: Vec2f, 103 | thickness: float32 = 1, color: Rgba32f = colRed) = 104 | 105 | gTracers[gTracerCount] = Tracer(tags: tags, color: color, 106 | thickness: thickness, 107 | kind: trkLine, 108 | lineA: a, lineB: b) 109 | inc(gTracerCount) 110 | 111 | proc traceRectangle*(tags: set[TracerTag], rectangle: Rectf, 112 | thickness: float32 = 1, color: Rgba32f = colRed) = 113 | 114 | gTracers[gTracerCount] = Tracer(tags: tags, color: color, 115 | thickness: thickness, 116 | kind: trkRectangle, 117 | rectangle: rectangle) 118 | inc(gTracerCount) 119 | 120 | proc traceCircle*(tags: set[TracerTag], center: Vec2f, radius: float32, 121 | thickness: float32 = 1, color: Rgba32f = colRed) = 122 | 123 | gTracers[gTracerCount] = Tracer(tags: tags, color: color, 124 | thickness: thickness, 125 | kind: trkCircle, 126 | circleCenter: center, 127 | circleRadius: radius) 128 | inc(gTracerCount) 129 | 130 | proc traceText*(tags: set[TracerTag], position: Vec2f, text: string, 131 | horzAlignment = taLeft, vertAlignment = taTop, 132 | alignBox = vec2f(0), color = colWhite) = 133 | 134 | gTracers[gTracerCount] = Tracer(tags: tags, color: color, 135 | kind: trkText, 136 | textPosition: position, 137 | text: text, 138 | textAlignment: (horzAlignment, 139 | vertAlignment), 140 | textAlignBox: alignBox) 141 | inc(gTracerCount) 142 | 143 | {.pop.} 144 | 145 | proc tracers*(graphics: Graphics, tags: set[TracerTag], 146 | textFont: Font = nil) = 147 | 148 | for index in 0.. 0: 45 | t[0] = -t[0] 46 | t[1] = -t[1] 47 | t[0] = t[0].normalize 48 | t[1] = t[1].normalize 49 | t[0] *= thickness / 2 50 | t[1] *= thickness / 2 51 | 52 | let 53 | a0 = a + t[0] 54 | a1 = a - t[0] 55 | c0 = c + t[1] 56 | c1 = c - t[1] 57 | aT = b + t[0] 58 | bT = b + t[1] 59 | (_, vPv) = lineIntersect(a + t[0], b + t[0], c + t[1], b + t[1]) 60 | nvP = b - (vPv - b) 61 | a0v = graphics.addVertex(a0, color) 62 | a1v = graphics.addVertex(a1, color) 63 | c0v = graphics.addVertex(c0, color) 64 | c1v = graphics.addVertex(c1, color) 65 | aTv = graphics.addVertex(aT, color) 66 | bTv = graphics.addVertex(bT, color) 67 | nvPv = graphics.addVertex(nvP, color) 68 | bv = graphics.addVertex(b, color) 69 | 70 | let 71 | intersection = lineIntersect(c + t[1], c - t[1], b - t[0], a - t[0]) 72 | degenerated = intersection[0] == irInsideBoth 73 | 74 | if not degenerated: 75 | graphics.addIndices([a0v, a1v, nvPv, a0v, nvPv, aTv]) 76 | graphics.addIndices([c0v, c1v, nvPv, c0v, nvPv, bTv]) 77 | else: 78 | let tp = graphics.addVertex(intersection[1], color) 79 | graphics.addIndices([a0v, a1v, bTv, bTv, aTv, a0v]) 80 | graphics.addIndices([tp, bTv, c0v]) 81 | 82 | let 83 | jointBottomVertex = 84 | if degenerated: bv 85 | else: nvPv 86 | case join 87 | of ljBevel, ljMiter: 88 | if not degenerated: 89 | graphics.addIndices([jointBottomVertex, aTv, bTv]) 90 | if join == ljMiter: 91 | let vPv = graphics.addVertex(vPv, color) 92 | graphics.addIndices([vPv, aTv, bTv]) 93 | of ljRound: 94 | let 95 | startAngle = angle(aT - b) 96 | endAngle = angle(bT - b) 97 | pointCount = 98 | int(float32(endAngle - startAngle).abs * (thickness / 2)) 99 | # ↓ this is a global for memory efficiency 100 | var rimIndices {.global, threadvar.}: seq[VertexIndex] 101 | rimIndices.setLen(0) 102 | rimIndices.add(aTv) 103 | for pointIndex in 0..= points.len: 128 | i -= points.len 129 | points[i] 130 | 131 | let lastIndex = points.len - 1 - ord(not close) * 2 132 | for index in 0..lastIndex: 133 | let 134 | b = getPoint(index + 1) 135 | a = 136 | if index == 0 and not close: 137 | var point = getPoint(index) 138 | if not close and cap == lcSquare: 139 | squareCap(point, b, thickness / 2) 140 | point 141 | else: (getPoint(index) + b) / 2 142 | c = 143 | if index == lastIndex and not close: 144 | var point = getPoint(index + 2) 145 | if cap == lcSquare: 146 | squareCap(point, b, thickness / 2) 147 | point 148 | else: 149 | (getPoint(index + 2) + b) / 2 150 | anchor(graphics, a, b, c, thickness, join, color) 151 | 152 | proc roundCap(graphics: Graphics, cap, next: Vec2f, 153 | radius: float32, color: Rgba32f) {.nimcall.} = 154 | ## Adds an arc at ``cap`` facing opposite of ``next`` with the given 155 | ## ``radius`` to create a round cap. 156 | let 157 | direction = cap - next 158 | angle = direction.angle 159 | angleCcw = angle - Radians(Pi / 2) 160 | graphics.arc(cap, radius, angleCcw, angleCcw + Pi.Radians, color, 161 | points = PolygonPoints(max(6, 2 * Pi * radius * 0.25))) 162 | 163 | if cap == lcRound: 164 | roundCap(graphics, points[0], points[1], thickness / 2, color) 165 | roundCap(graphics, points[^1], points[^2], thickness / 2, color) 166 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/transform.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | 22 | import 23 | types, vect, bb 24 | 25 | ## / Identity transform matrix. 26 | 27 | var cpTransformIdentity*: cpTransform = cpTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0) 28 | 29 | ## / Construct a new transform matrix. 30 | ## / (a, b) is the x basis vector. 31 | ## / (c, d) is the y basis vector. 32 | ## / (tx, ty) is the translation. 33 | 34 | proc cpTransformNew*(a: cpFloat; b: cpFloat; c: cpFloat; d: cpFloat; tx: cpFloat; 35 | ty: cpFloat): cpTransform {.inline.} = 36 | var t: cpTransform = cpTransform(a: a, b: b, c: c, d: d, tx: tx, ty: ty) 37 | return t 38 | 39 | ## / Construct a new transform matrix in transposed order. 40 | 41 | proc cpTransformNewTranspose*(a: cpFloat; c: cpFloat; tx: cpFloat; b: cpFloat; 42 | d: cpFloat; ty: cpFloat): cpTransform {.inline.} = 43 | var t: cpTransform = cpTransform(a: a, b: b, c: c, d: d, tx: tx, ty: ty) 44 | return t 45 | 46 | ## / Get the inverse of a transform matrix. 47 | 48 | proc cpTransformInverse*(t: cpTransform): cpTransform {.inline.} = 49 | var inv_det: cpFloat = 1.0 / (t.a * t.d - t.c * t.b) 50 | return cpTransformNewTranspose(t.d * inv_det, -(t.c * inv_det), 51 | (t.c * t.ty - t.tx * t.d) * inv_det, -(t.b * inv_det), 52 | t.a * inv_det, (t.tx * t.b - t.a * t.ty) * inv_det) 53 | 54 | ## / Multiply two transformation matrices. 55 | 56 | proc cpTransformMult*(t1: cpTransform; t2: cpTransform): cpTransform {.inline.} = 57 | return cpTransformNewTranspose(t1.a * t2.a + t1.c * t2.b, t1.a * t2.c + t1.c * t2.d, 58 | t1.a * t2.tx + t1.c * t2.ty + t1.tx, 59 | t1.b * t2.a + t1.d * t2.b, t1.b * t2.c + t1.d * t2.d, 60 | t1.b * t2.tx + t1.d * t2.ty + t1.ty) 61 | 62 | ## / Transform an absolute point. (i.e. a vertex) 63 | 64 | proc cpTransformPoint*(t: cpTransform; p: cpVect): cpVect {.inline.} = 65 | return cpv(t.a * p.x + t.c * p.y + t.tx, t.b * p.x + t.d * p.y + t.ty) 66 | 67 | ## / Transform a vector (i.e. a normal) 68 | 69 | proc cpTransformVect*(t: cpTransform; v: cpVect): cpVect {.inline.} = 70 | return cpv(t.a * v.x + t.c * v.y, t.b * v.x + t.d * v.y) 71 | 72 | ## / Transform a cpBB. 73 | 74 | proc cpTransformbBB*(t: cpTransform; bb: cpBB): cpBB {.inline.} = 75 | var center: cpVect = cpBBCenter(bb) 76 | var hw: cpFloat = (bb.r - bb.l) * 0.5 77 | var hh: cpFloat = (bb.t - bb.b) * 0.5 78 | var 79 | a: cpFloat = t.a * hw 80 | b: cpFloat = t.c * hh 81 | d: cpFloat = t.b * hw 82 | e: cpFloat = t.d * hh 83 | var hw_max: cpFloat = cpfmax(cpfabs(a + b), cpfabs(a - b)) 84 | var hh_max: cpFloat = cpfmax(cpfabs(d + e), cpfabs(d - e)) 85 | return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max) 86 | 87 | ## / Create a transation matrix. 88 | 89 | proc cpTransformTranslate*(translate: cpVect): cpTransform {.inline.} = 90 | return cpTransformNewTranspose(1.0, 0.0, translate.x, 0.0, 1.0, translate.y) 91 | 92 | ## / Create a scale matrix. 93 | 94 | proc cpTransformScale*(scaleX: cpFloat; scaleY: cpFloat): cpTransform {.inline.} = 95 | return cpTransformNewTranspose(scaleX, 0.0, 0.0, 0.0, scaleY, 0.0) 96 | 97 | ## / Create a rotation matrix. 98 | 99 | proc cpTransformRotate*(radians: cpFloat): cpTransform {.inline.} = 100 | var rot: cpVect = cpvforangle(radians) 101 | return cpTransformNewTranspose(rot.x, -rot.y, 0.0, rot.y, rot.x, 0.0) 102 | 103 | ## / Create a rigid transformation matrix. (transation + rotation) 104 | 105 | proc cpTransformRigid*(translate: cpVect; radians: cpFloat): cpTransform {.inline.} = 106 | var rot: cpVect = cpvforangle(radians) 107 | return cpTransformNewTranspose(rot.x, -rot.y, translate.x, rot.y, rot.x, translate.y) 108 | 109 | ## / Fast inverse of a rigid transformation matrix. 110 | 111 | proc cpTransformRigidInverse*(t: cpTransform): cpTransform {.inline.} = 112 | return cpTransformNewTranspose(t.d, -t.c, (t.c * t.ty - t.tx * t.d), -t.b, t.a, 113 | (t.tx * t.b - t.a * t.ty)) 114 | 115 | ## MARK: Miscellaneous (but useful) transformation matrices. 116 | ## See source for documentation... 117 | 118 | proc cpTransformWrap*(outer: cpTransform; inner: cpTransform): cpTransform {.inline.} = 119 | return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer)) 120 | 121 | proc cpTransformWrapInverse*(outer: cpTransform; inner: cpTransform): cpTransform {. 122 | inline.} = 123 | return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer))) 124 | 125 | proc cpTransformOrtho*(bb: cpBB): cpTransform {.inline.} = 126 | return cpTransformNewTranspose(2.0 / (bb.r - bb.l), 0.0, 127 | -((bb.r + bb.l) / (bb.r - bb.l)), 0.0, 128 | 2.0 / (bb.t - bb.b), 129 | -((bb.t + bb.b) / (bb.t - bb.b))) 130 | 131 | proc cpTransformBoneScale*(v0: cpVect; v1: cpVect): cpTransform {.inline.} = 132 | var d: cpVect = cpvsub(v1, v0) 133 | return cpTransformNewTranspose(d.x, -d.y, v0.x, d.y, d.x, v0.y) 134 | 135 | proc cpTransformAxialScale*(axis: cpVect; pivot: cpVect; scale: cpFloat): cpTransform {. 136 | inline.} = 137 | var A: cpFloat = axis.x * axis.y * (scale - 1.0) 138 | var B: cpFloat = cpvdot(axis, pivot) * (1.0 - scale) 139 | return cpTransformNewTranspose(scale * axis.x * axis.x + axis.y * axis.y, A, axis.x * B, A, 140 | axis.x * axis.x + scale * axis.y * axis.y, axis.y * B) 141 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/constraint.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | ## / @defgroup cpConstraint cpConstraint 22 | ## / @{ 23 | ## / Callback function type that gets called before solving a joint. 24 | 25 | import types 26 | 27 | type 28 | cpConstraintPreSolveFunc* = proc (constraint: ptr cpConstraint; space: ptr cpSpace) 29 | 30 | ## / Callback function type that gets called after solving a joint. 31 | 32 | type 33 | cpConstraintPostSolveFunc* = proc (constraint: ptr cpConstraint; space: ptr cpSpace) 34 | 35 | ## / Destroy a constraint. 36 | 37 | proc cpConstraintDestroy*(constraint: ptr cpConstraint) {. 38 | importc: "cpConstraintDestroy", header: "".} 39 | ## / Destroy and free a constraint. 40 | 41 | proc cpConstraintFree*(constraint: ptr cpConstraint) {.importc: "cpConstraintFree", 42 | header: "".} 43 | ## / Get the cpSpace this constraint is added to. 44 | 45 | proc cpConstraintGetSpace*(constraint: ptr cpConstraint): ptr cpSpace {. 46 | importc: "cpConstraintGetSpace", header: "".} 47 | ## / Get the first body the constraint is attached to. 48 | 49 | proc cpConstraintGetBodyA*(constraint: ptr cpConstraint): ptr cpBody {. 50 | importc: "cpConstraintGetBodyA", header: "".} 51 | ## / Get the second body the constraint is attached to. 52 | 53 | proc cpConstraintGetBodyB*(constraint: ptr cpConstraint): ptr cpBody {. 54 | importc: "cpConstraintGetBodyB", header: "".} 55 | ## / Get the maximum force that this constraint is allowed to use. 56 | 57 | proc cpConstraintGetMaxForce*(constraint: ptr cpConstraint): cpFloat {. 58 | importc: "cpConstraintGetMaxForce", header: "".} 59 | ## / Set the maximum force that this constraint is allowed to use. (defaults to INFINITY) 60 | 61 | proc cpConstraintSetMaxForce*(constraint: ptr cpConstraint; maxForce: cpFloat) {. 62 | importc: "cpConstraintSetMaxForce", header: "".} 63 | ## / Get rate at which joint error is corrected. 64 | 65 | proc cpConstraintGetErrorBias*(constraint: ptr cpConstraint): cpFloat {. 66 | importc: "cpConstraintGetErrorBias", header: "".} 67 | ## / Set rate at which joint error is corrected. 68 | ## / Defaults to pow(1.0 - 0.1, 60.0) meaning that it will 69 | ## / correct 10% of the error every 1/60th of a second. 70 | 71 | proc cpConstraintSetErrorBias*(constraint: ptr cpConstraint; errorBias: cpFloat) {. 72 | importc: "cpConstraintSetErrorBias", header: "".} 73 | ## / Get the maximum rate at which joint error is corrected. 74 | 75 | proc cpConstraintGetMaxBias*(constraint: ptr cpConstraint): cpFloat {. 76 | importc: "cpConstraintGetMaxBias", header: "".} 77 | ## / Set the maximum rate at which joint error is corrected. (defaults to INFINITY) 78 | 79 | proc cpConstraintSetMaxBias*(constraint: ptr cpConstraint; maxBias: cpFloat) {. 80 | importc: "cpConstraintSetMaxBias", header: "".} 81 | ## / Get if the two bodies connected by the constraint are allowed to collide or not. 82 | 83 | proc cpConstraintGetCollideBodies*(constraint: ptr cpConstraint): cpBool {. 84 | importc: "cpConstraintGetCollideBodies", header: "".} 85 | ## / Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse) 86 | 87 | proc cpConstraintSetCollideBodies*(constraint: ptr cpConstraint; 88 | collideBodies: cpBool) {. 89 | importc: "cpConstraintSetCollideBodies", header: "".} 90 | ## / Get the pre-solve function that is called before the solver runs. 91 | 92 | proc cpConstraintGetPreSolveFunc*(constraint: ptr cpConstraint): cpConstraintPreSolveFunc {. 93 | importc: "cpConstraintGetPreSolveFunc", header: "".} 94 | ## / Set the pre-solve function that is called before the solver runs. 95 | 96 | proc cpConstraintSetPreSolveFunc*(constraint: ptr cpConstraint; 97 | preSolveFunc: cpConstraintPreSolveFunc) {. 98 | importc: "cpConstraintSetPreSolveFunc", header: "".} 99 | ## / Get the post-solve function that is called before the solver runs. 100 | 101 | proc cpConstraintGetPostSolveFunc*(constraint: ptr cpConstraint): cpConstraintPostSolveFunc {. 102 | importc: "cpConstraintGetPostSolveFunc", header: "".} 103 | ## / Set the post-solve function that is called before the solver runs. 104 | 105 | proc cpConstraintSetPostSolveFunc*(constraint: ptr cpConstraint; 106 | postSolveFunc: cpConstraintPostSolveFunc) {. 107 | importc: "cpConstraintSetPostSolveFunc", header: "".} 108 | ## / Get the user definable data pointer for this constraint 109 | 110 | proc cpConstraintGetUserData*(constraint: ptr cpConstraint): cpDataPointer {. 111 | importc: "cpConstraintGetUserData", header: "".} 112 | ## / Set the user definable data pointer for this constraint 113 | 114 | proc cpConstraintSetUserData*(constraint: ptr cpConstraint; userData: cpDataPointer) {. 115 | importc: "cpConstraintSetUserData", header: "".} 116 | ## / Get the last impulse applied by this constraint. 117 | 118 | proc cpConstraintGetImpulse*(constraint: ptr cpConstraint): cpFloat {. 119 | importc: "cpConstraintGetImpulse", header: "".} 120 | 121 | include 122 | pin_joint, slide_joint, pivot_joint, groove_joint, damped_spring, 123 | damped_rotary_spring, rotary_limit_joint, ratchet_joint, gear_joint, 124 | simple_motor 125 | 126 | ## /@} 127 | -------------------------------------------------------------------------------- /src/rapid/wrappers/chipmunk/vect.nim: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2013 Scott Lembcke and Howling Moon Software 2 | ## 3 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 4 | ## of this software and associated documentation files (the "Software"), to deal 5 | ## in the Software without restriction, including without limitation the rights 6 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | ## copies of the Software, and to permit persons to whom the Software is 8 | ## furnished to do so, subject to the following conditions: 9 | ## 10 | ## The above copyright notice and this permission notice shall be included in 11 | ## all copies or substantial portions of the Software. 12 | ## 13 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | ## SOFTWARE. 20 | ## 21 | 22 | import std/math 23 | 24 | import types 25 | 26 | ## / @defgroup cpVect cpVect 27 | ## / Chipmunk's 2D vector type along with a handy 2D vector math lib. 28 | ## / @{ 29 | ## / Constant for the zero vector. 30 | 31 | var cpvzero*: cpVect = cpVect(x: 0.0, y: 0.0) 32 | 33 | ## / Convenience constructor for cpVect structs. 34 | 35 | proc cpv*(x: cpFloat; y: cpFloat): cpVect {.inline.} = 36 | var v: cpVect = cpVect(x: x, y: y) 37 | return v 38 | 39 | ## / Check if two vectors are equal. (Be careful when comparing floating point numbers!) 40 | 41 | proc cpveql*(v1: cpVect; v2: cpVect): bool {.inline.} = 42 | return v1.x == v2.x and v1.y == v2.y 43 | 44 | ## / Add two vectors 45 | 46 | proc cpvadd*(v1: cpVect; v2: cpVect): cpVect {.inline.} = 47 | return cpv(v1.x + v2.x, v1.y + v2.y) 48 | 49 | ## / Subtract two vectors. 50 | 51 | proc cpvsub*(v1: cpVect; v2: cpVect): cpVect {.inline.} = 52 | return cpv(v1.x - v2.x, v1.y - v2.y) 53 | 54 | ## / Negate a vector. 55 | 56 | proc cpvneg*(v: cpVect): cpVect {.inline.} = 57 | return cpv(-v.x, -v.y) 58 | 59 | ## / Scalar multiplication. 60 | 61 | proc cpvmult*(v: cpVect; s: cpFloat): cpVect {.inline.} = 62 | return cpv(v.x * s, v.y * s) 63 | 64 | ## / Vector dot product. 65 | 66 | proc cpvdot*(v1: cpVect; v2: cpVect): cpFloat {.inline.} = 67 | return v1.x * v2.x + v1.y * v2.y 68 | 69 | ## / 2D vector cross product analog. 70 | ## / The cross product of 2D vectors results in a 3D vector with only a z component. 71 | ## / This function returns the magnitude of the z value. 72 | 73 | proc cpvcross*(v1: cpVect; v2: cpVect): cpFloat {.inline.} = 74 | return v1.x * v2.y - v1.y * v2.x 75 | 76 | ## / Returns a perpendicular vector. (90 degree rotation) 77 | 78 | proc cpvperp*(v: cpVect): cpVect {.inline.} = 79 | return cpv(-v.y, v.x) 80 | 81 | ## / Returns a perpendicular vector. (-90 degree rotation) 82 | 83 | proc cpvrperp*(v: cpVect): cpVect {.inline.} = 84 | return cpv(v.y, -v.x) 85 | 86 | ## / Returns the vector projection of v1 onto v2. 87 | 88 | proc cpvproject*(v1: cpVect; v2: cpVect): cpVect {.inline.} = 89 | return cpvmult(v2, cpvdot(v1, v2) / cpvdot(v2, v2)) 90 | 91 | ## / Returns the unit length vector for the given angle (in radians). 92 | 93 | proc cpvforangle*(a: cpFloat): cpVect {.inline.} = 94 | return cpv(cos(a), sin(a)) 95 | 96 | ## / Returns the angular direction v is pointing in (in radians). 97 | 98 | proc cpvtoangle*(v: cpVect): cpFloat {.inline.} = 99 | return arctan2(v.y, v.x) 100 | 101 | ## / Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. 102 | 103 | proc cpvrotate*(v1: cpVect; v2: cpVect): cpVect {.inline.} = 104 | return cpv(v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x) 105 | 106 | ## / Inverse of cpvrotate(). 107 | 108 | proc cpvunrotate*(v1: cpVect; v2: cpVect): cpVect {.inline.} = 109 | return cpv(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y) 110 | 111 | ## / Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. 112 | 113 | proc cpvlengthsq*(v: cpVect): cpFloat {.inline.} = 114 | return cpvdot(v, v) 115 | 116 | ## / Returns the length of v. 117 | 118 | proc cpvlength*(v: cpVect): cpFloat {.inline.} = 119 | return sqrt(cpvdot(v, v)) 120 | 121 | ## / Linearly interpolate between v1 and v2. 122 | 123 | proc cpvlerp*(v1: cpVect; v2: cpVect; t: cpFloat): cpVect {.inline.} = 124 | return cpvadd(cpvmult(v1, 1.0 - t), cpvmult(v2, t)) 125 | 126 | ## / Returns a normalized copy of v. 127 | 128 | proc cpvnormalize*(v: cpVect): cpVect {.inline.} = 129 | ## Neat trick I saw somewhere to avoid div/0. 130 | return cpvmult(v, 1.0 / (cpvlength(v) + 0.000001)) 131 | 132 | ## / Spherical linearly interpolate between v1 and v2. 133 | 134 | proc cpvslerp*(v1: cpVect; v2: cpVect; t: cpFloat): cpVect {.inline.} = 135 | var dot: cpFloat = cpvdot(cpvnormalize(v1), cpvnormalize(v2)) 136 | var omega: cpFloat = arccos(cpfclamp(dot, -1.0, 1.0)) 137 | if omega < 0.001: 138 | ## If the angle between two vectors is very small, lerp instead to avoid precision issues. 139 | return cpvlerp(v1, v2, t) 140 | else: 141 | var denom: cpFloat = 1.0 / sin(omega) 142 | return cpvadd(cpvmult(v1, sin((1.0 - t) * omega) * denom), 143 | cpvmult(v2, sin(t * omega) * denom)) 144 | 145 | ## / Spherical linearly interpolate between v1 towards v2 by no more than angle a radians 146 | 147 | proc cpvslerpconst*(v1: cpVect; v2: cpVect; a: cpFloat): cpVect {.inline.} = 148 | var dot: cpFloat = cpvdot(cpvnormalize(v1), cpvnormalize(v2)) 149 | var omega: cpFloat = arccos(cpfclamp(dot, -1.0, 1.0)) 150 | return cpvslerp(v1, v2, cpfmin(a, omega) / omega) 151 | 152 | ## / Clamp v to length len. 153 | 154 | proc cpvclamp*(v: cpVect; len: cpFloat): cpVect {.inline.} = 155 | return if (cpvdot(v, v) > len * len): cpvmult(cpvnormalize(v), len) else: v 156 | 157 | ## / Linearly interpolate between v1 towards v2 by distance d. 158 | 159 | proc cpvlerpconst*(v1: cpVect; v2: cpVect; d: cpFloat): cpVect {.inline.} = 160 | return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d)) 161 | 162 | ## / Returns the distance between v1 and v2. 163 | 164 | proc cpvdist*(v1: cpVect; v2: cpVect): cpFloat {.inline.} = 165 | return cpvlength(cpvsub(v1, v2)) 166 | 167 | ## / Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. 168 | 169 | proc cpvdistsq*(v1: cpVect; v2: cpVect): cpFloat {.inline.} = 170 | return cpvlengthsq(cpvsub(v1, v2)) 171 | 172 | ## / Returns true if the distance between v1 and v2 is less than dist. 173 | 174 | proc cpvnear*(v1: cpVect; v2: cpVect; dist: cpFloat): bool {.inline.} = 175 | return cpvdistsq(v1, v2) < dist * dist 176 | 177 | ## / @} 178 | ## / @defgroup cpMat2x2 cpMat2x2 179 | ## / 2x2 matrix type used for tensors and such. 180 | ## / @{ 181 | ## NUKE 182 | 183 | proc cpMat2x2New*(a: cpFloat; b: cpFloat; c: cpFloat; d: cpFloat): cpMat2x2 {.inline.} = 184 | var m: cpMat2x2 = cpMat2x2(a: a, b: b, c: c, d: d) 185 | return m 186 | 187 | proc cpMat2x2Transform*(m: cpMat2x2; v: cpVect): cpVect {.inline.} = 188 | return cpv(v.x * m.a + v.y * m.b, v.x * m.c + v.y * m.d) 189 | 190 | ## /@} 191 | -------------------------------------------------------------------------------- /src/rapid/ecs/system_macro.nim: -------------------------------------------------------------------------------- 1 | ## ``system`` macro for defining ECS systems. 2 | 3 | import std/macros 4 | import std/strformat 5 | import std/strutils 6 | import std/tables 7 | 8 | import common 9 | 10 | macro addRequire(sysName: static string, 11 | name: untyped{ident}, fullType: typed) = 12 | ## Auxiliary macro used to resolve the type ``ty`` before adding a require to 13 | ## a system. 14 | 15 | let ty = fullType.extractVarType() 16 | if ty.symKind != nskType: 17 | error("type expected", ty) 18 | ecsSystems[sysName].requires.add(newIdentDefs(name, fullType)) 19 | 20 | proc genDocComment(sys: SystemInfo): NimNode = 21 | ## Generates a doc comment for a system. 22 | 23 | const 24 | RequiresHeader = "Requires:" 25 | ImplementsHeader = "Implements:" 26 | 27 | var doc = sys.doc.strip 28 | 29 | doc.add(&"\n\n**{RequiresHeader}** ") 30 | for i, req in sys.requires: 31 | let ty = req[1] 32 | if ty.kind != nnkSym: continue 33 | let 34 | owner = ty.owner 35 | name = 36 | if owner != nil and owner.symKind == nskModule: 37 | fmt"{owner.repr}.{ty.repr}" 38 | else: 39 | ty.repr 40 | doc.add(&"``{name}``") 41 | if i != sys.requires.len - 1: 42 | doc.add(", ") 43 | 44 | doc.add(&"\n\n**{ImplementsHeader}**\n\n") 45 | doc.add(".. code-block:: nim") 46 | for i, impl in sys.implements: 47 | var decl = copy(impl.abstract) 48 | decl[^1] = newEmptyNode() 49 | doc.add("\n " & decl.repr) 50 | 51 | result = newNimNode(nnkCommentStmt) 52 | result.strVal = doc 53 | 54 | macro genSystemDoc(sysName: static string) = 55 | ## Generates a dummy const with a system's documentation. 56 | 57 | let sys = ecsSystems[sysName] 58 | result = newProc(name = postfix(ident("system:" & sysName), "*")) 59 | result.body.add(genDocComment(sys)) 60 | # ↓ unfortunately this is not possible, since it doesn't play well with 61 | # ecs_macro.useSym 62 | # result.addPragma(newColonExpr(ident"error", 63 | # newLit("systems cannot be called"))) 64 | 65 | macro semcheckEndpointImpl(sysName: static string, implIndex: static int, 66 | blockWithProc: typed) = 67 | ## Semchecks an endpoint implementation and saves it in the ``concrete`` field 68 | ## of ``ecsSystem[sysName].implements[implIndex]``. 69 | 70 | blockWithProc.expectKind({nnkBlockStmt, nnkBlockExpr}) 71 | # skip if the concrete implementation is nil (because @world was used) 72 | if blockWithProc.kind == nnkBlockExpr: 73 | return 74 | blockWithProc[1].expectKind(nnkProcDef) 75 | 76 | ecsSystems[sysName].implements[implIndex].concrete = blockWithProc[1] 77 | 78 | macro checkSystem(sysName: static string) = 79 | ## Type checks a system's procedures. 80 | 81 | result = newStmtList() 82 | 83 | let sys = ecsSystems[sysName] 84 | for index, impl in sys.implements: 85 | if impl.abstract.params[0].kind != nnkEmpty: 86 | error("endpoint implementations must not return anything", 87 | impl.abstract.params[0]) 88 | let concrete = getConcrete(impl.abstract, sys) 89 | result.add(newCall(bindSym"semcheckEndpointImpl", 90 | newLit(sysName), newLit(index), newBlockStmt(concrete))) 91 | 92 | macro system*(name: untyped{ident}, body: untyped{nkStmtList}) = 93 | ## Defines an ECS system. The body accepts: 94 | ## 95 | ## - doc comments describing the system's behavior 96 | ## - a list of name-component bindings for components required by the system. 97 | ## these take the form of ``var`` or ``let`` variable declarations, eg. 98 | ## ``let gravity: Gravity`` and ``var physics: PhysicsBody``. ``var`` 99 | ## signifies that the component will be mutated, and ``let`` signifies that 100 | ## the component will only be read. 101 | ## two special bindings may be used: ``: @world``, and 102 | ## ``: @entity``. these refer to the world and entity types generated 103 | ## by the ECS macro. 104 | ## - any number of implemented system interface procedures (endpoints). 105 | ## these may use any requires declared in the ``requires`` section. 106 | ## 107 | ## The macro will generate documentation about the system's requires and 108 | ## endpoints. Unfortunately, Nim does not allow for easily creating your own 109 | ## doc sections, so the documentation is put under the Procs section, and the 110 | ## procedure's name is prefixed with ``system:``. The resulting ``proc`` 111 | ## cannot be called—attempting to do so will result in a compilation error. 112 | ## 113 | ## **Remarks:** 114 | ## 115 | ## Requiring an ``@entity`` without a ``@world`` is pretty much useless, 116 | ## as all entity-related actions (such as getting components) also require 117 | ## the world the entity belongs to (as that's what stores all the 118 | ## actual data). 119 | ## Requiring a ``@world`` disables all declaration-time checks. Just like with 120 | ## generics, there's no way of knowing what the world type is—what components 121 | ## it has, what system interface procs it implements, etc.—so all early checks 122 | ## are delayed to the ``ecs`` macro. This has the drawback of symbols not 123 | ## being bound early, so usage of any symbols from external modules will also 124 | ## require these modules to be imported in the module that declares the ECS 125 | ## world. 126 | 127 | runnableExamples: 128 | import rapid/ecs/components/physics 129 | 130 | system exampleGravity: 131 | requires: 132 | physics: PhysicsBody 133 | gravity: Gravity 134 | 135 | proc update*(step: float32) = 136 | physics.acceleration += gravity.gravity 137 | 138 | let sysName = name.strVal 139 | result = newStmtList() 140 | if sysName in ecsSystems: 141 | error("redefinition of system " & sysName, name) 142 | var sys = SystemInfo() 143 | 144 | var requireList: NimNode 145 | 146 | for stmt in body: 147 | stmt.expectKind({nnkCall, nnkProcDef, nnkCommentStmt}) 148 | case stmt.kind 149 | of nnkCall: 150 | stmt[0].expectIdent("requires") 151 | requireList = stmt[1] 152 | of nnkProcDef: 153 | sys.implements.add(SystemImpl(abstract: stmt)) 154 | of nnkCommentStmt: 155 | sys.doc.add(stmt.strVal & '\n') 156 | else: assert false, "unreachable" 157 | 158 | if requireList == nil: 159 | error("missing require list", body) 160 | 161 | proc require(result: var NimNode, paramName, ty: NimNode, isVar: bool) = 162 | if ty.kind == nnkIdent: 163 | let varTy = 164 | if isVar: newTree(nnkVarTy, ty) 165 | else: ty 166 | result.add(newCall(bindSym"addRequire", newLit(sysName), 167 | paramName, varTy)) 168 | else: 169 | let varTy = 170 | if isVar: newTree(nnkVarTy, ident(ty.repr)) 171 | else: ident(ty.repr) 172 | sys.requires.add(newIdentDefs(paramName, varTy)) 173 | 174 | for req in requireList: 175 | req.expectKind({nnkVarSection, nnkLetSection}) 176 | for defs in req: 177 | defs[0].expectKind({nnkIdent, nnkAccQuoted}) 178 | defs[1].expectKind({nnkIdent, nnkPrefix}) 179 | defs[2].expectKind(nnkEmpty) 180 | let 181 | paramName = defs[0] 182 | ty = defs[1] 183 | result.require(paramName, ty, isVar = req.kind == nnkVarSection) 184 | 185 | ecsSystems[sysName] = sys 186 | result.add(newCall(bindSym"genSystemDoc", newLit(sysName))) 187 | result.add(newCall(bindSym"checkSystem", newLit(sysName))) 188 | 189 | when isMainModule: 190 | import glm/vec 191 | 192 | import components/physics 193 | 194 | system applyGravity: 195 | ## Applies gravity to physics bodies. 196 | 197 | requires: 198 | var physics: PhysicsBody 199 | let gravity: Gravity 200 | 201 | proc update() = 202 | physics.acceleration += gravity.force 203 | -------------------------------------------------------------------------------- /src/rapid/ec.nim: -------------------------------------------------------------------------------- 1 | ## Entity-component framework. Object oriented alternative to rapid/ecs for the 2 | ## time being, as rapid/ecs is not really usable at this point in time, 3 | ## and I don't have the knowledge nor time needed to continue its development. 4 | ## 5 | ## This framework assumes you're using the rapid/game module. 6 | 7 | import std/macros 8 | 9 | import aglet 10 | import graphics/context 11 | 12 | type 13 | RootComponent* {.byref.} = object of RootObj 14 | ## Empty component, base for inheriting from. 15 | 16 | impl*: ComponentImpl ## interface implementation 17 | 18 | ComponentUpdate*[C: RootComponent] = 19 | proc (comp: var C) {.nimcall.} 20 | ## Component tick callback. 21 | 22 | ComponentDraw*[C: RootComponent] = 23 | proc (comp: var C, target: Target, step: float32) {.nimcall.} 24 | ## Component aglet-draw callback. 25 | 26 | ComponentRender*[C: RootComponent] = 27 | proc (comp: var C, target: Target, graphics: Graphics, 28 | step: float32) {.nimcall.} 29 | ## Component rapid-render callback. 30 | 31 | ComponentShape*[C: RootComponent] = 32 | proc (comp: var C, graphics: Graphics, step: float32) {.nimcall.} 33 | ## Component shape-render callback. 34 | 35 | ComponentImpl* = object 36 | ## An object holding all the callbacks a component can implement. 37 | 38 | deleteEntity: bool 39 | # this is perhaps not very elegant, but i couldn't find a nicer solution 40 | # for deleting entities inside of a component 41 | 42 | update*: ComponentUpdate[RootComponent] 43 | ## Ticks the component once. This runs a fixed amount of times per second 44 | ## (60 by default, but this can change depending on the game). 45 | 46 | draw*: ComponentDraw[RootComponent] 47 | render*: ComponentRender[RootComponent] 48 | shape*: ComponentShape[RootComponent] 49 | ## Components can implement either draw, render or shape, 50 | ## depending on the need. These endpoints are triggered by the respective 51 | ## Entity procs, and have the following semantics: 52 | ## 53 | ## - draw should be used when drawing raw aglet objects, eg. Meshes. 54 | ## This is called "aglet-drawing" 55 | ## - render should be used when you need a Graphics context but also need 56 | ## to invoke a draw call. This is called "rapid-rendering" 57 | ## - shape should be used when rendering using a Graphics context only. 58 | ## This is called "shape-rendering" 59 | 60 | RootEntity* = ref object of RootObj 61 | ## Empty entity, base for inheriting from. 62 | 63 | delete: bool 64 | components: seq[ptr RootComponent] 65 | # ↑ a bit non-idiomatic, but should suffice until lent in object fields is 66 | # implemented. you don't use this directly anyways. 67 | 68 | 69 | # component essentials 70 | 71 | iterator components*(entity: RootEntity): var RootComponent = 72 | ## Iterates through the entity's registered components. 73 | 74 | for comp in entity.components: 75 | yield comp[] 76 | 77 | proc registerComponent*[T: RootComponent](entity: RootEntity, 78 | comp: ptr T) {.inline.} = 79 | ## Registers an entity's component. You usually don't need to use this, 80 | ## as this is handled by registerComponents. 81 | 82 | entity.components.add(comp) 83 | 84 | proc registerComponents*[T: RootEntity](entity: T) {.inline.} = 85 | ## Initializes an entity by registering all of its components. 86 | ## This **must** always be done after constructing an entity, otherwise 87 | ## component callbacks won't run. 88 | 89 | for name, value in fieldPairs(entity[]): 90 | when value is RootComponent: 91 | entity.registerComponent(addr value) 92 | 93 | proc deleteEntity*(comp: var RootComponent) = 94 | ## Marks the parent entity for deletion. 95 | comp.impl.deleteEntity = true 96 | 97 | 98 | # callbacks 99 | 100 | {.push inline.} 101 | 102 | proc onUpdate*[T: RootComponent](comp: var T, impl: ComponentUpdate[T]) = 103 | comp.impl.update = cast[ComponentUpdate[RootComponent]](impl) 104 | 105 | proc onDraw*[T: RootComponent](comp: var T, impl: ComponentDraw[T]) = 106 | comp.impl.draw = cast[ComponentDraw[RootComponent]](impl) 107 | 108 | proc onRender*[T: RootComponent](comp: var T, impl: ComponentRender[T]) = 109 | comp.impl.render = cast[ComponentRender[RootComponent]](impl) 110 | 111 | proc onShape*[T: RootComponent](comp: var T, impl: ComponentShape[T]) = 112 | ## Shortcuts for setting ``comp.impl`` fields manually, for use with either 113 | ## the ``do`` notation or overload resolution. 114 | comp.impl.shape = cast[ComponentShape[RootComponent]](impl) 115 | 116 | proc autoImplement*[T: RootComponent](comp: var T) = 117 | ## Automagically implement any callbacks, depending on what procs have been 118 | ## declared at callsite, eg. if ``proc componentUpdate(comp: var T)`` is 119 | ## declared, it will automatically be implemented for the given component 120 | ## instance. 121 | ## 122 | ## **Debugging:** If you ever need to debug which procs were implemented, pass 123 | ## ``-d:rapidECDebugAutoImplement`` to the compiler. 124 | 125 | template attempt(stmt) = 126 | when compiles(stmt): 127 | stmt 128 | 129 | template report(callbackName) = 130 | when defined(rapidECDebugAutoImplement): 131 | {.hint: "rapid/ec autoImplement: found " & callbackName.} 132 | 133 | attempt: 134 | mixin componentUpdate 135 | comp.onUpdate componentUpdate 136 | report "update" 137 | attempt: 138 | mixin componentDraw 139 | comp.onDraw componentDraw 140 | report "draw" 141 | attempt: 142 | mixin componentRender 143 | comp.onRender componentRender 144 | report "render" 145 | attempt: 146 | mixin componentShape 147 | comp.onShape componentShape 148 | report "shape" 149 | 150 | proc update*(entity: RootEntity) = 151 | ## Tick all of the entity's components update routines once. 152 | ## This also checks for deletion flags and updates the entity's deletion flag 153 | ## accordingly. If any of the entity's components' deletion flags is set, 154 | ## components that have been registered after it are not updated, because the 155 | ## entity is about to be deleted in the current tick anyways. 156 | 157 | for comp in components(entity): 158 | if comp.impl.update != nil: 159 | comp.impl.update(comp) 160 | if comp.impl.deleteEntity: 161 | entity.delete = true 162 | break 163 | 164 | proc draw*(entity: RootEntity, target: Target, step: float32) = 165 | ## aglet-draw all of the entity's components. 166 | ## 167 | ## Explanations of what the difference between draw/render/shape is can be 168 | ## found above. 169 | 170 | for comp in components(entity): 171 | if comp.impl.draw != nil: 172 | comp.impl.draw(comp, target, step) 173 | 174 | proc render*(entity: RootEntity, target: Target, graphics: Graphics, 175 | step: float32) = 176 | ## rapid-render all of the entity's components. 177 | 178 | for comp in components(entity): 179 | if comp.impl.render != nil: 180 | comp.impl.render(comp, target, graphics, step) 181 | 182 | proc shape*(entity: RootEntity, graphics: Graphics, step: float32) = 183 | ## Shape-render all of the entity's components. 184 | 185 | for comp in components(entity): 186 | if comp.impl.shape != nil: 187 | comp.impl.shape(comp, graphics, step) 188 | 189 | proc cleanup*(entities: var seq[RootEntity]) = 190 | ## Cleans up any entities that are marked for deletion. 191 | 192 | var 193 | len = entities.len 194 | i = 0 195 | while i < len: 196 | let entity = entities[i] 197 | if entity.delete: 198 | entities.del(i) 199 | dec len 200 | continue 201 | inc i 202 | 203 | proc update*(entities: seq[RootEntity]) = 204 | ## Ticks all the entities in the sequence. 205 | 206 | for entity in entities: 207 | entity.update() 208 | 209 | proc updateAndCleanup*(entities: var seq[RootEntity]) = 210 | ## Ticks all the entities in the sequence, then cleans up any entities marked 211 | ## for deletion. 212 | 213 | entities.update() 214 | entities.cleanup() 215 | 216 | proc draw*(entities: seq[RootEntity], target: Target, step: float32) = 217 | ## aglet-draws all the entities in the sequence. 218 | 219 | for entity in entities: 220 | entity.draw(target, step) 221 | 222 | proc render*(entities: seq[RootEntity], target: Target, graphics: Graphics, 223 | step: float32) = 224 | ## rapid-renders all the entities in the sequence. 225 | 226 | for entity in entities: 227 | entity.render(target, graphics, step) 228 | 229 | proc shape*(entities: seq[RootEntity], graphics: Graphics, step: float32) = 230 | ## Shape-renders all the entities in the sequence. 231 | 232 | for entity in entities: 233 | entity.shape(graphics, step) 234 | 235 | {.pop.} 236 | 237 | 238 | # tests 239 | 240 | when isMainModule: 241 | 242 | type 243 | CompPhysics = object of RootComponent 244 | position, velocity, acceleration: Vec2f 245 | 246 | Player = ref object of RootEntity 247 | physics: CompPhysics 248 | 249 | proc update(physics: var CompPhysics) = 250 | physics.velocity += physics.acceleration 251 | physics.acceleration *= 0 252 | physics.position += physics.velocity 253 | 254 | proc physics*(position: Vec2f, 255 | velocity, acceleration = vec2f(0)): CompPhysics = 256 | result = CompPhysics(position: position, velocity: velocity, 257 | acceleration: acceleration) 258 | result.autoImplement() 259 | 260 | var player = Player(physics: physics(position = vec2f(32, 32), 261 | velocity = vec2f(1, 0))) 262 | player.registerComponents() 263 | 264 | let initialPosition = player.physics.position 265 | const tickCount = 100 266 | for tick in 1..tickCount: 267 | player.update() 268 | block: 269 | let physics = player.physics 270 | assert physics.position == initialPosition + physics.velocity * tickCount 271 | echo "test passed! position = ", physics.position 272 | -------------------------------------------------------------------------------- /src/rapid/graphics/postprocess.nim: -------------------------------------------------------------------------------- 1 | ## Post-processing filters and a ping-pong buffer. 2 | 3 | import std/tables 4 | 5 | import aglet 6 | from aglet/gl import OpenGl 7 | 8 | import context 9 | 10 | type 11 | EffectVertex = object 12 | position, uv: Vec2f 13 | 14 | PostProcess* = distinct Program[EffectVertex] 15 | ## Post-processing effect. 16 | 17 | EffectBuffer* = ref object 18 | ## Ping-pong framebuffer for post-processing effects. 19 | window: Window 20 | size: Vec2i 21 | colorTargetCount: Positive 22 | hdr: bool 23 | a, b: MultiFramebuffer 24 | fullscreenRect: Mesh[EffectVertex] 25 | drawParams: DrawParams 26 | passthrough: Program[EffectVertex] 27 | 28 | EffectTarget* = object of Target 29 | ## Target for rendering to an effect buffer. 30 | buffer: EffectBuffer 31 | 32 | const 33 | PostProcessVertexShader = glsl""" 34 | #version 330 core 35 | 36 | in vec2 position; 37 | in vec2 uv; 38 | 39 | uniform vec2 bufferSize; 40 | 41 | out vec2 bufferUv; 42 | out vec2 pixelPosition; 43 | 44 | void main(void) { 45 | gl_Position = vec4(position, 0.0, 1.0); 46 | 47 | bufferUv = uv; 48 | 49 | vec2 invertedUv = uv; 50 | invertedUv.y = 1.0 - uv.y; 51 | pixelPosition = invertedUv * bufferSize; 52 | } 53 | """ 54 | PassthroughVertexShader = glsl""" 55 | #version 330 core 56 | 57 | in vec2 position; 58 | in vec2 uv; 59 | 60 | out vec2 bufferUv; 61 | 62 | void main(void) { 63 | gl_Position = vec4(position, 0.0, 1.0); 64 | bufferUv = uv; 65 | } 66 | """ 67 | PassthroughFragmentShader = glsl""" 68 | #version 330 core 69 | 70 | in vec2 bufferUv; 71 | 72 | uniform sampler2D buffer; 73 | 74 | out vec4 color; 75 | 76 | void main(void) { 77 | color = texture(buffer, bufferUv); 78 | } 79 | """ 80 | 81 | proc newPostProcess*(window: Window, source: GlslSource): PostProcess = 82 | ## Creates a new post-processing shader program. 83 | ## ``source`` is a string contatining GLSL source code for the fragment 84 | ## shader. The fragment shader has some extra input variables it can use: 85 | ## 86 | ## - ``in vec2 bufferUv`` – UV coordinates for sampling from 87 | ## the ``buffer`` texture 88 | ## - ``in vec2 pixelPosition`` – ``textureUv``, but transformed to 89 | ## screen coordinates 90 | ## - ``uniform vec2 bufferSize`` – the effect buffer's size 91 | ## - ``uniform sampler2D buffer`` – the effect buffer texture 92 | 93 | window.newProgram[:EffectVertex](PostProcessVertexShader, source).PostProcess 94 | 95 | proc size*(buffer: EffectBuffer): Vec2i {.inline.} = 96 | ## Returns the size of the effect buffer as a vector. 97 | buffer.size 98 | 99 | proc width*(buffer: EffectBuffer): int32 {.inline.} = 100 | ## Returns the width of the effect buffer. 101 | buffer.size.x 102 | 103 | proc height*(buffer: EffectBuffer): int32 {.inline.} = 104 | ## Returns the height of the effect buffer. 105 | buffer.size.y 106 | 107 | proc drawParams*(buffer: EffectBuffer): DrawParams {.inline.} = 108 | ## Returns the draw params used when applying effects. 109 | buffer.drawParams 110 | 111 | proc `drawParams=`*(buffer: EffectBuffer, params: DrawParams) {.inline.} = 112 | ## Sets the draw params used when applying effects. 113 | buffer.drawParams = params 114 | 115 | template createColor(window: Window, size: Vec2i, hdr: bool): ColorSource = 116 | 117 | if hdr: 118 | window.newTexture2D[:Rgba32f](size).source 119 | else: 120 | window.newTexture2D[:Rgba8](size).source 121 | 122 | template createFramebuffer(window: Window, size: Vec2i, 123 | colorTargetCount: Positive, 124 | hdr: bool): MultiFramebuffer = 125 | 126 | var colorSources: seq[ColorSource] 127 | for i in 0..: sampler2D`` – samplers for the effect buffer's color 210 | ## targets, where ``n`` is a 0-based number which is the index of the 211 | ## color target. If there is only one target, it's simply called 212 | ## ``buffer``. 213 | ## 214 | ## This procedure also accepts parameters for how the color attachments should 215 | ## be sampled by the ``?buffer`` uniform. 216 | 217 | var 218 | bTarget = buffer.b.render() 219 | samplers {.global, threadvar.}: Table[string, Uniform] 220 | 221 | # this is perhaps a bit inefficient 222 | samplers.clear() 223 | if buffer.colorTargetCount > 1: 224 | for colorTarget in 0..".} 35 | ## / Override the restitution (elasticity) that will be applied to the pair of colliding objects. 36 | 37 | proc cpArbiterSetRestitution*(arb: ptr cpArbiter; restitution: cpFloat) {. 38 | importc: "cpArbiterSetRestitution", header: "".} 39 | ## / Get the friction coefficient that will be applied to the pair of colliding objects. 40 | 41 | proc cpArbiterGetFriction*(arb: ptr cpArbiter): cpFloat {. 42 | importc: "cpArbiterGetFriction", header: "".} 43 | ## / Override the friction coefficient that will be applied to the pair of colliding objects. 44 | 45 | proc cpArbiterSetFriction*(arb: ptr cpArbiter; friction: cpFloat) {. 46 | importc: "cpArbiterSetFriction", header: "".} 47 | ## Get the relative surface velocity of the two shapes in contact. 48 | 49 | proc cpArbiterGetSurfaceVelocity*(arb: ptr cpArbiter): cpVect {. 50 | importc: "cpArbiterGetSurfaceVelocity", header: "".} 51 | ## Override the relative surface velocity of the two shapes in contact. 52 | ## By default this is calculated to be the difference of the two surface velocities clamped to the tangent plane. 53 | 54 | proc cpArbiterSetSurfaceVelocity*(arb: ptr cpArbiter; vr: cpVect) {. 55 | importc: "cpArbiterSetSurfaceVelocity", header: "".} 56 | ## / Get the user data pointer associated with this pair of colliding objects. 57 | 58 | proc cpArbiterGetUserData*(arb: ptr cpArbiter): cpDataPointer {. 59 | importc: "cpArbiterGetUserData", header: "".} 60 | ## / Set a user data point associated with this pair of colliding objects. 61 | ## / If you need to perform any cleanup for this pointer, you must do it yourself, in the separate callback for instance. 62 | 63 | proc cpArbiterSetUserData*(arb: ptr cpArbiter; userData: cpDataPointer) {. 64 | importc: "cpArbiterSetUserData", header: "".} 65 | ## / Calculate the total impulse including the friction that was applied by this arbiter. 66 | ## / This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. 67 | 68 | proc cpArbiterTotalImpulse*(arb: ptr cpArbiter): cpVect {. 69 | importc: "cpArbiterTotalImpulse", header: "".} 70 | ## / Calculate the amount of energy lost in a collision including static, but not dynamic friction. 71 | ## / This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. 72 | 73 | proc cpArbiterTotalKE*(arb: ptr cpArbiter): cpFloat {.importc: "cpArbiterTotalKE", 74 | header: "".} 75 | ## / Mark a collision pair to be ignored until the two objects separate. 76 | ## / Pre-solve and post-solve callbacks will not be called, but the separate callback will be called. 77 | 78 | proc cpArbiterIgnore*(arb: ptr cpArbiter): cpBool {.importc: "cpArbiterIgnore", 79 | header: "".} 80 | ## / Return the colliding shapes involved for this arbiter. 81 | ## / The order of their cpSpace.collision_type values will match 82 | ## / the order set when the collision handler was registered. 83 | 84 | proc cpArbiterGetShapes*(arb: ptr cpArbiter; a: ptr ptr cpShape; b: ptr ptr cpShape) {. 85 | importc: "cpArbiterGetShapes", header: "".} 86 | ## / A macro shortcut for defining and retrieving the shapes from an arbiter. 87 | ## #define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__); 88 | 89 | proc cpArbiterGetBodies*(arb: ptr cpArbiter; a: ptr ptr cpBody; b: ptr ptr cpBody) {. 90 | importc: "cpArbiterGetBodies", header: "".} 91 | ## / A macro shortcut for defining and retrieving the bodies from an arbiter. 92 | ## #define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__); 93 | ## / A struct that wraps up the important collision data for an arbiter. 94 | 95 | type 96 | INNER_C_STRUCT_cpArbiter_88* {.importc: "no_name", header: "", bycopy.} = object 97 | pointA* {.importc: "pointA".}: cpVect ## / The position of the contact on the surface of each shape. 98 | pointB* {.importc: "pointB".}: cpVect ## / Penetration distance of the two shapes. Overlapping means it will be negative. 99 | ## / This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet(). 100 | distance* {.importc: "distance".}: cpFloat 101 | 102 | cpContactPointSet* {.importc: "cpContactPointSet", header: "", bycopy.} = object 103 | count* {.importc: "count".}: cint ## / The number of contact points in the set. 104 | ## / The normal of the collision. 105 | normal* {.importc: "normal".}: cpVect ## / The array of contact points. 106 | points* {.importc: "points".}: array[CP_MAX_CONTACTS_PER_ARBITER, 107 | INNER_C_STRUCT_cpArbiter_88] 108 | 109 | 110 | ## / Return a contact set from an arbiter. 111 | 112 | proc cpArbiterGetContactPointSet*(arb: ptr cpArbiter): cpContactPointSet {. 113 | importc: "cpArbiterGetContactPointSet", header: "".} 114 | ## / Replace the contact point set for an arbiter. 115 | ## / This can be a very powerful feature, but use it with caution! 116 | 117 | proc cpArbiterSetContactPointSet*(arb: ptr cpArbiter; set: ptr cpContactPointSet) {. 118 | importc: "cpArbiterSetContactPointSet", header: "".} 119 | ## / Returns true if this is the first step a pair of objects started colliding. 120 | 121 | proc cpArbiterIsFirstContact*(arb: ptr cpArbiter): cpBool {. 122 | importc: "cpArbiterIsFirstContact", header: "".} 123 | ## / Returns true if the separate callback is due to a shape being removed from the space. 124 | 125 | proc cpArbiterIsRemoval*(arb: ptr cpArbiter): cpBool {.importc: "cpArbiterIsRemoval", 126 | header: "".} 127 | ## / Get the number of contact points for this arbiter. 128 | 129 | proc cpArbiterGetCount*(arb: ptr cpArbiter): cint {.importc: "cpArbiterGetCount", 130 | header: "".} 131 | ## / Get the normal of the collision. 132 | 133 | proc cpArbiterGetNormal*(arb: ptr cpArbiter): cpVect {.importc: "cpArbiterGetNormal", 134 | header: "".} 135 | ## / Get the position of the @c ith contact point on the surface of the first shape. 136 | 137 | proc cpArbiterGetPointA*(arb: ptr cpArbiter; i: cint): cpVect {. 138 | importc: "cpArbiterGetPointA", header: "".} 139 | ## / Get the position of the @c ith contact point on the surface of the second shape. 140 | 141 | proc cpArbiterGetPointB*(arb: ptr cpArbiter; i: cint): cpVect {. 142 | importc: "cpArbiterGetPointB", header: "".} 143 | ## / Get the depth of the @c ith contact point. 144 | 145 | proc cpArbiterGetDepth*(arb: ptr cpArbiter; i: cint): cpFloat {. 146 | importc: "cpArbiterGetDepth", header: "".} 147 | ## / If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. 148 | ## / You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. 149 | 150 | proc cpArbiterCallWildcardBeginA*(arb: ptr cpArbiter; space: ptr cpSpace): cpBool {. 151 | importc: "cpArbiterCallWildcardBeginA", header: "".} 152 | ## / If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. 153 | ## / You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. 154 | 155 | proc cpArbiterCallWildcardBeginB*(arb: ptr cpArbiter; space: ptr cpSpace): cpBool {. 156 | importc: "cpArbiterCallWildcardBeginB", header: "".} 157 | ## / If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. 158 | ## / You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. 159 | 160 | proc cpArbiterCallWildcardPreSolveA*(arb: ptr cpArbiter; space: ptr cpSpace): cpBool {. 161 | importc: "cpArbiterCallWildcardPreSolveA", header: "".} 162 | ## / If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. 163 | ## / You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. 164 | 165 | proc cpArbiterCallWildcardPreSolveB*(arb: ptr cpArbiter; space: ptr cpSpace): cpBool {. 166 | importc: "cpArbiterCallWildcardPreSolveB", header: "".} 167 | ## / If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. 168 | 169 | proc cpArbiterCallWildcardPostSolveA*(arb: ptr cpArbiter; space: ptr cpSpace) {. 170 | importc: "cpArbiterCallWildcardPostSolveA", header: "".} 171 | ## / If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. 172 | 173 | proc cpArbiterCallWildcardPostSolveB*(arb: ptr cpArbiter; space: ptr cpSpace) {. 174 | importc: "cpArbiterCallWildcardPostSolveB", header: "".} 175 | ## / If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. 176 | 177 | proc cpArbiterCallWildcardSeparateA*(arb: ptr cpArbiter; space: ptr cpSpace) {. 178 | importc: "cpArbiterCallWildcardSeparateA", header: "".} 179 | ## / If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. 180 | 181 | proc cpArbiterCallWildcardSeparateB*(arb: ptr cpArbiter; space: ptr cpSpace) {. 182 | importc: "cpArbiterCallWildcardSeparateB", header: "".} 183 | ## / @} 184 | --------------------------------------------------------------------------------