Compare commits

..

No commits in common. "master" and "v0.7.11" have entirely different histories.

33 changed files with 730 additions and 1195 deletions

View file

@ -1,6 +1,6 @@
# Nim bindings for Godot Engine
# Nim bindings for Godot Engine [![Build Status](https://travis-ci.org/pragmagic/godot-nim.svg?branch=master)](https://travis-ci.org/pragmagic/godot-nim)
Nim bindings for Godot 3 based on [GDNative](https://godotengine.org/article/dlscript-here).
Nim bindings for Godot 3.0 based on [GDNative](https://godotengine.org/article/dlscript-here).
Documentation: https://pragmagic.github.io/godot-nim/

View file

@ -71,7 +71,7 @@ proc fixupHrefs(file: string) =
proc getGitHash(): string =
result = execProcess("git rev-parse HEAD")
if result.len > 0:
if not result.isNil and result.len > 0:
result = result.replace("\L", "").replace("\r", "")
proc buildDocs*(outDir, godotNimDir, godotBin: string) =
@ -104,15 +104,14 @@ proc buildDocs*(outDir, godotNimDir, godotBin: string) =
createDir(godotApiFolder)
genApiFiles(godotApiFolder, godotBinAbs)
for file in walkDirRec(godotApiFolder, {pcFile, pcDir}, [".nim"]):
spawn execOrFail("nim doc -d:useRealtimeGc --path:godot -o:" &
quoted(outName(godotApiFolder, file)) & ' ' & quoted(file))
spawn execOrFail("nim doc -d:useRealtimeGc --path:godot " & quoted(file))
sync()
var gitHash: string
withDir(godotBinAbs.parentDir()):
gitHash = getGitHash()
if gitHash.len == 0:
if gitHash.isNil or gitHash.len == 0:
raise newException(Exception,
"Godot executable is not under Git repository")

View file

@ -1,4 +1,4 @@
version = "0.8.2"
version = "0.7.11"
author = "Xored Software, Inc."
description = "Godot Engine bindings"
license = "MIT"

View file

@ -1,9 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import vector3
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
proc initAABB*(pos, size: Vector3): AABB {.inline.} =
@ -12,9 +9,6 @@ proc initAABB*(pos, size: Vector3): AABB {.inline.} =
proc `$`*(self: AABB): string {.inline.} =
$getGDNativeAPI().aabbAsString(self)
proc hash*(self: AABB): Hash {.inline.} =
!$(self.position.hash() !& self.size.hash())
proc area*(self: AABB): float32 {.inline.} =
getGDNativeAPI().aabbGetArea(self)
@ -31,10 +25,10 @@ proc encloses*(self, other: AABB): bool {.inline.} =
getGDNativeAPI().aabbEncloses(self, other)
proc merge*(self, other: AABB): AABB {.inline.} =
getGDNativeAPI().aabbMerge(self, other).toAABB()
getGDNativeAPI().aabbMerge(self, other)
proc intersection*(self, other: AABB): AABB {.inline.} =
getGDNativeAPI().aabbIntersection(self, other).toAABB()
getGDNativeAPI().aabbIntersection(self, other)
proc intersectsPlane*(self: AABB; plane: Plane): bool {.inline.} =
getGDNativeAPI().aabbIntersectsPlane(self, plane)
@ -46,10 +40,10 @@ proc contains*(self: AABB; point: Vector3): bool {.inline.} =
getGDNativeAPI().aabbHasPoint(self, point)
proc getSupport*(self: AABB; dir: Vector3): Vector3 {.inline.} =
getGDNativeAPI().aabbGetSupport(self, dir).toVector3()
getGDNativeAPI().aabbGetSupport(self, dir)
proc getLongestAxis*(self: AABB): Vector3 {.inline.} =
getGDNativeAPI().aabbGetLongestAxis(self).toVector3()
getGDNativeAPI().aabbGetLongestAxis(self)
proc getLongestAxisIndex*(self: AABB): cint {.inline.} =
getGDNativeAPI().aabbGetLongestAxisIndex(self)
@ -58,7 +52,7 @@ proc getLongestAxisSize*(self: AABB): float32 {.inline.} =
getGDNativeAPI().aabbGetLongestAxisSize(self)
proc getShortestAxis*(self: AABB): Vector3 {.inline.} =
getGDNativeAPI().aabbGetShortestAxis(self).toVector3()
getGDNativeAPI().aabbGetShortestAxis(self)
proc getShortestAxisIndex*(self: AABB): cint {.inline.} =
getGDNativeAPI().aabbGetShortestAxisIndex(self)
@ -67,13 +61,13 @@ proc getShortestAxisSize*(self: AABB): float32 {.inline.} =
getGDNativeAPI().aabbGetShortestAxisSize(self)
proc expand*(self: AABB; toPoint: Vector3): AABB {.inline.} =
getGDNativeAPI().aabbExpand(self, toPoint).toAABB()
getGDNativeAPI().aabbExpand(self, toPoint)
proc grow*(self: AABB; by: float32): AABB {.inline.} =
getGDNativeAPI().aabbGrow(self, by).toAABB()
getGDNativeAPI().aabbGrow(self, by)
proc getEndpoint*(self: AABB; idx: cint): Vector3 {.inline.} =
getGDNativeAPI().aabbGetEndpoint(self, idx).toVector3()
getGDNativeAPI().aabbGetEndpoint(self, idx)
proc `==`*(a, b: AABB): bool {.inline.} =
getGDNativeAPI().aabbOperatorEqual(a, b)

View file

@ -1,7 +1,7 @@
# Copyright 2018 Xored Software, Inc.
import hashes
import internal/godotinternaltypes, internal/godotarrays
import internal.godotinternaltypes, internal.godotarrays
type
Array* = ref object

View file

@ -1,7 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import math, hashes
import math
import godotbase, vector3, quats
import godotcoretypes
@ -202,9 +201,6 @@ proc `$`*(self: Basis): string {.inline.} =
result.add(", ")
result.add($self[i][j])
proc hash*(self: Basis): Hash {.inline.} =
!$(self.elements[0].hash() !& self.elements[1].hash() !& self.elements[2].hash())
template cofac(row1, col1, row2, col2: int): float32 =
self.elements[row1][col1] * self.elements[row2][col2] -
self.elements[row1][col2] * self.elements[row2][col1]

View file

@ -1,7 +1,5 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import godotcoretypes, gdnativeapi
proc initColor*(r, g, b: float32; a: float32 = 1.0'f32): Color {.inline.} =
@ -35,9 +33,6 @@ proc `$`*(self: Color): string {.inline.} =
result.add(", ")
result.add($self.a)
proc hash*(self: Color): Hash {.inline.} =
!$(self.r.hash() !& self.g.hash() !& self.b.hash() !& self.a.hash())
proc toHex(val: float32, target: var string, targetIdx: int) =
let val = clamp(int(val * 255), 0, 255)
let nums = [(val and 0xF0) shr 4, val and 0xF]
@ -64,16 +59,16 @@ proc gray*(self: Color): float32 {.inline.} =
getGDNativeAPI().colorGray(self)
proc inverted*(self: Color): Color {.inline.} =
getGDNativeAPI().colorInverted(self).toColor()
getGDNativeAPI().colorInverted(self)
proc contrasted*(self: Color): Color {.inline.} =
getGDNativeAPI().colorContrasted(self).toColor()
getGDNativeAPI().colorContrasted(self)
proc lerp*(self: Color; b: Color; t: float32): Color {.inline.} =
getGDNativeAPI().colorLinearInterpolate(self, b, t).toColor()
getGDNativeAPI().colorLinearInterpolate(self, b, t)
proc blend*(self: Color; over: Color): Color {.inline.} =
getGDNativeAPI().colorBlend(self, over).toColor()
getGDNativeAPI().colorBlend(self, over)
proc `==`*(a, b: Color): bool {.inline.} =
a.r == b.r and a.g == b.g and a.b == b.b and a.a == b.a

View file

@ -1,8 +1,8 @@
# Copyright 2018 Xored Software, Inc.
import hashes
import internal/godotinternaltypes, internal/godotdictionaries,
internal/godotvariants, internal/godotstrings
import internal.godotinternaltypes, internal.godotdictionaries,
internal.godotvariants, internal.godotstrings
type
Dictionary* = ref object
@ -33,9 +33,6 @@ proc isEmpty*(self: Dictionary): bool {.inline.} =
proc clear*(self: Dictionary) {.inline.} =
self.godotDictionary.clear()
proc hash*(self: Dictionary): Hash {.inline.} =
hash(self.godotDictionary.godotHash())
import variants, arrays
proc newVariant*(dict: Dictionary): Variant {.inline.} =
@ -43,7 +40,7 @@ proc newVariant*(dict: Dictionary): Variant {.inline.} =
initGodotVariant(result.godotVariant[], dict.godotDictionary)
proc asDictionary*(self: Variant): Dictionary {.inline.} =
newDictionary(self.godotVariant[].asGodotDictionary())
newDictionary(self.godotVariant[].asGodotDictionary)
proc contains*(self: Dictionary; key: Variant): bool {.inline.}=
self.godotDictionary.contains(key.godotVariant[])
@ -54,6 +51,9 @@ proc contains*(self: Dictionary; keys: Array): bool {.inline.} =
proc del*(self: Dictionary; key: Variant) {.inline.} =
self.godotDictionary.del(key.godotVariant[])
proc hash*(self: Dictionary): Hash {.inline.} =
hash(self.godotDictionary.godotHash())
proc keys*(self: Dictionary): Array {.inline.} =
newArray(self.godotDictionary.keys())

View file

@ -30,9 +30,6 @@ proc stepify*(value, step: float32): float32 {.inline, noinit.} =
else:
value
when (NimMajor, NimMinor, NimPatch) < (0, 20, 4):
# Newer Nim has these procs in system module
proc min*(x, y: float32): float32 {.inline, noinit.} =
if x <= y: x else: y

View file

@ -1,9 +1,7 @@
# Copyright 2018 Xored Software, Inc.
import hashes
import internal/godotinternaltypes, internal/godotnodepaths,
internal/godotstrings
import internal.godotinternaltypes, internal.godotnodepaths,
internal.godotstrings
type
NodePath* = ref object
@ -34,9 +32,6 @@ proc `$`*(self: NodePath): string {.inline.} =
result = $s
s.deinit()
proc hash*(self: NodePath): Hash {.inline.} =
($self).hash()
proc isAbsolute*(self: NodePath): bool {.inline.} =
self.path.isAbsolute()

View file

@ -1,10 +1,8 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import vector3
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
proc initPlane*(a, b, c, d: float32): Plane {.inline.} =
@ -22,17 +20,14 @@ proc initPlane*(normal: Vector3; d: float32): Plane {.inline.} =
proc `$`*(self: Plane): string {.inline.} =
$getGDNativeAPI().planeAsString(self)
proc hash*(self: Plane): Hash {.inline, noinit.} =
!$(self.normal.hash() !& self.d.hash())
proc normalized*(self: Plane): Plane {.inline.} =
getGDNativeAPI().planeNormalized(self).toPlane()
getGDNativeAPI().planeNormalized(self)
proc center*(self: Plane): Vector3 {.inline.} =
getGDNativeAPI().planeCenter(self).toVector3()
getGDNativeAPI().planeCenter(self)
proc getAnyPoint*(self: Plane): Vector3 {.inline.} =
getGDNativeAPI().planeGetAnyPoint(self).toVector3()
getGDNativeAPI().planeGetAnyPoint(self)
proc isPointOver*(self: Plane; point: Vector3): bool {.inline.} =
getGDNativeAPI().planeIsPointOver(self, point)
@ -45,7 +40,7 @@ proc contains*(self: Plane; point: Vector3;
getGDNativeAPI().planeHasPoint(self, point, epsilon)
proc project*(self: Plane; point: Vector3): Vector3 {.inline.} =
getGDNativeAPI().planeProject(self, point).toVector3()
getGDNativeAPI().planeProject(self, point)
proc intersect3*(self: Plane; dest: var Vector3;
b, c: Plane): bool {.inline.} =
@ -60,7 +55,7 @@ proc intersectsSegment*(self: Plane; dest: var Vector3;
getGDNativeAPI().planeIntersectsSegment(self, dest, segmentBegin, segmentEnd)
proc `-`*(self: Plane): Plane {.inline.} =
getGDNativeAPI().planeOperatorNeg(self).toPlane()
getGDNativeAPI().planeOperatorNeg(self)
proc `==`*(a, b: Plane): bool {.inline.} =
getGDNativeAPI().planeOperatorEqual(a, b)

View file

@ -1,11 +1,8 @@
# Copyright 2018 Xored Software, Inc.
import hashes
import godotcoretypes
import internal/godotinternaltypes, internal/godotpoolarrays,
internal/godotstrings
import vector2, vector3, colors
import internal.godotinternaltypes, internal.godotpoolarrays,
internal.godotstrings
template definePoolArrayBase(T, GodotT, DataT, fieldName, newProcName,
initProcName) =
@ -29,36 +26,6 @@ template definePoolArrayBase(T, GodotT, DataT, fieldName, newProcName,
new(result, poolArrayFinalizer)
result.fieldName = arr
proc hash*(arr: T): Hash =
for item in arr.fieldName.items():
result = result !&
(when type(item) is GodotString: ($item).hash() else: item.hash())
result = !$result
definePoolArrayBase(PoolByteArray, GodotPoolByteArray, uint8,
godotPoolByteArray, newPoolByteArray,
initGodotPoolByteArray)
definePoolArrayBase(PoolIntArray, GodotPoolIntArray, cint,
godotPoolIntArray, newPoolIntArray,
initGodotPoolIntArray)
definePoolArrayBase(PoolRealArray, GodotPoolRealArray, float32,
godotPoolRealArray, newPoolRealArray,
initGodotPoolRealArray)
definePoolArrayBase(PoolVector2Array, GodotPoolVector2Array, Vector2,
godotPoolVector2Array, newPoolVector2Array,
initGodotPoolVector2Array)
definePoolArrayBase(PoolVector3Array, GodotPoolVector3Array, Vector3,
godotPoolVector3Array, newPoolVector3Array,
initGodotPoolVector3Array)
definePoolArrayBase(PoolColorArray, GodotPoolColorArray, Color,
godotPoolColorArray, newPoolColorArray,
initGodotPoolColorArray)
definePoolArrayBase(PoolStringArray, GodotPoolStringArray, string,
godotPoolStringArray, newPoolStringArray,
initGodotPoolStringArray)
import arrays
template definePoolArray(T, GodotT, DataT, fieldName, newProcName, initProcName;
noData = false) =
@ -116,6 +83,30 @@ template definePoolArray(T, GodotT, DataT, fieldName, newProcName, initProcName;
for pair in arr.fieldName.mpairs:
yield (pair[0].int, pair[1])
definePoolArrayBase(PoolByteArray, GodotPoolByteArray, uint8,
godotPoolByteArray, newPoolByteArray,
initGodotPoolByteArray)
definePoolArrayBase(PoolIntArray, GodotPoolIntArray, cint,
godotPoolIntArray, newPoolIntArray,
initGodotPoolIntArray)
definePoolArrayBase(PoolRealArray, GodotPoolRealArray, float32,
godotPoolRealArray, newPoolRealArray,
initGodotPoolRealArray)
definePoolArrayBase(PoolVector2Array, GodotPoolVector2Array, Vector2,
godotPoolVector2Array, newPoolVector2Array,
initGodotPoolVector2Array)
definePoolArrayBase(PoolVector3Array, GodotPoolVector3Array, Vector3,
godotPoolVector3Array, newPoolVector3Array,
initGodotPoolVector3Array)
definePoolArrayBase(PoolColorArray, GodotPoolColorArray, Color,
godotPoolColorArray, newPoolColorArray,
initGodotPoolColorArray)
definePoolArrayBase(PoolStringArray, GodotPoolStringArray, string,
godotPoolStringArray, newPoolStringArray,
initGodotPoolStringArray)
import arrays
definePoolArray(PoolByteArray, GodotPoolByteArray, uint8,
godotPoolByteArray, newPoolByteArray,
initGodotPoolByteArray)

View file

@ -1,8 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
proc initQuat*(x, y, z, w: float32): Quat {.inline.} =
@ -19,9 +17,6 @@ proc initQuat*(axis: Vector3; angle: float32): Quat {.inline.} =
proc `$`*(self: Quat): string {.inline.} =
$getGDNativeAPI().quatAsString(self)
proc hash*(self: Quat): Hash {.inline.} =
!$(self.x.hash() !& self.y.hash() !& self.z.hash() !& self.w.hash())
proc length*(self: Quat): float32 {.inline.} =
getGDNativeAPI().quatLength(self)
@ -29,50 +24,50 @@ proc lengthSquared*(self: Quat): float32 {.inline.} =
getGDNativeAPI().quatLengthSquared(self)
proc normalized*(self: Quat): Quat {.inline.} =
getGDNativeAPI().quatNormalized(self).toQuat()
getGDNativeAPI().quatNormalized(self)
proc isNormalized*(self: Quat): bool {.inline.} =
getGDNativeAPI().quatIsNormalized(self)
proc inverse*(self: Quat): Quat {.inline.} =
getGDNativeAPI().quatInverse(self).toQuat()
getGDNativeAPI().quatInverse(self)
proc dot*(a, b: Quat): float32 {.inline.} =
getGDNativeAPI().quatDot(a, b)
proc xform*(self: Quat; v: Vector3): Vector3 {.inline.} =
getGDNativeAPI().quatXform(self, v).toVector3()
getGDNativeAPI().quatXform(self, v)
proc slerp*(self: Quat; b: Quat; t: float32): Quat {.inline.} =
getGDNativeAPI().quatSlerp(self, b, t).toQuat()
getGDNativeAPI().quatSlerp(self, b, t)
proc slerpni*(self: Quat; b: Quat; t: float32): Quat {.inline.} =
getGDNativeAPI().quatSlerpni(self, b, t).toQuat()
getGDNativeAPI().quatSlerpni(self, b, t)
proc cubicSlerp*(self, b, preA, postB: Quat;
t: float32): Quat {.inline.} =
getGDNativeAPI().quatCubicSlerp(self, b, preA, postB, t).toQuat()
getGDNativeAPI().quatCubicSlerp(self, b, preA, postB, t)
proc `*`*(a: Quat, b: float32): Quat {.inline.} =
getGDNativeAPI().quatOperatorMultiply(a, b).toQuat()
getGDNativeAPI().quatOperatorMultiply(a, b)
proc `*=`*(a: var Quat, b: float32) {.inline.} =
a = a * b
proc `+`*(a, b: Quat): Quat {.inline.} =
getGDNativeAPI().quatOperatorAdd(a, b).toQuat()
getGDNativeAPI().quatOperatorAdd(a, b)
proc `+=`*(a: var Quat, b: Quat) {.inline.} =
a = a + b
proc `-`*(a, b: Quat): Quat {.inline.} =
getGDNativeAPI().quatOperatorSubtract(a, b).toQuat()
getGDNativeAPI().quatOperatorSubtract(a, b)
proc `-=`* (a: var Quat, b: Quat) {.inline.} =
a = a - b
proc `/`*(self: Quat; b: float32): Quat {.inline.} =
getGDNativeAPI().quatOperatorDivide(self, b).toQuat()
getGDNativeAPI().quatOperatorDivide(self, b)
proc `/=`*(self: var Quat, b: float32) {.inline.} =
self = self / b
@ -81,4 +76,4 @@ proc `==`*(a, b: Quat): bool {.inline.} =
getGDNativeAPI().quatOperatorEqual(a, b)
proc `-`*(self: Quat): Quat {.inline.} =
getGDNativeAPI().quatOperatorNeg(self).toQuat()
getGDNativeAPI().quatOperatorNeg(self)

View file

@ -1,9 +1,7 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import vector2
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
{.push stackTrace: off.}
@ -17,12 +15,9 @@ proc initRect2*(pos, size: Vector2): Rect2 {.inline, noinit.} =
proc initRect2*(x, y, sizeX, sizeY: float32): Rect2 {.inline, noinit.} =
Rect2(position: vec2(x, y), size: vec2(sizeX, sizeY))
proc `$`*(self: Rect2): string {.inline, noinit.} =
proc `$`*(self: Rect2): string {.inline.} =
$getGDNativeAPI().rect2AsString(self)
proc hash*(self: Rect2): Hash {.inline, noinit.} =
!$(self.position.hash() !& self.size.hash())
proc area*(self: Rect2): float32 {.inline, noinit.} =
self.size.x * self.size.y

View file

@ -1,8 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import godotcoretypes, internal/godotinternaltypes, gdnativeapi
import godotcoretypes, internal.godotinternaltypes, gdnativeapi
proc initRID*(): RID {.inline.} =
getGDNativeAPI().ridNew(result)
@ -16,9 +14,6 @@ proc id*(self: RID): uint32 {.inline.} =
proc `$`*(self: RID): string {.inline.} =
$self.id
proc hash*(self: RID): Hash {.inline.} =
self.id.hash()
proc `==`*(a, b: RID): bool {.inline.} =
getGDNativeAPI().ridOperatorEqual(a, b)

View file

@ -1,8 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes, vector2
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
proc initTransform2D*(): Transform2D {.inline.} =
@ -17,63 +15,60 @@ proc initTransform2D*(xAxis, yAxis, origin: Vector2): Transform2D {.inline.} =
proc `$`*(self: Transform2D): string {.inline.} =
$getGDNativeAPI().transform2DAsString(self)
proc hash*(self: Transform2D): Hash {.inline, noinit.} =
!$(self.elements[0].hash() !& self.elements[1].hash() !& self.elements[2].hash())
proc inverse*(self: Transform2D): Transform2D {.inline.} =
getGDNativeAPI().transform2DInverse(self).toTransform2D()
getGDNativeAPI().transform2DInverse(self)
proc affineInverse*(self: Transform2D): Transform2D {.inline.} =
getGDNativeAPI().transform2DAffineInverse(self).toTransform2D()
getGDNativeAPI().transform2DAffineInverse(self)
proc rotation*(self: Transform2D): float32 {.inline.} =
getGDNativeAPI().transform2DGetRotation(self)
proc origin*(self: Transform2D): Vector2 {.inline.} =
getGDNativeAPI().transform2DGetOrigin(self).toVector2()
getGDNativeAPI().transform2DGetOrigin(self)
proc scale*(self: Transform2D): Vector2 {.inline.} =
getGDNativeAPI().transform2DGetScale(self).toVector2()
getGDNativeAPI().transform2DGetScale(self)
proc orthonormalized*(self: Transform2D): Transform2D {.inline.} =
getGDNativeAPI().transform2DOrthonormalized(self).toTransform2D()
getGDNativeAPI().transform2DOrthonormalized(self)
proc rotated*(self: Transform2D; phi: float32): Transform2D {.inline.} =
getGDNativeAPI().transform2DRotated(self, phi).toTransform2D()
getGDNativeAPI().transform2DRotated(self, phi)
proc scaled*(self: Transform2D; scale: Vector2): Transform2D {.inline.} =
getGDNativeAPI().transform2DScaled(self, scale).toTransform2D()
getGDNativeAPI().transform2DScaled(self, scale)
proc translated*(self: Transform2D; offset: Vector2): Transform2D {.inline.} =
getGDNativeAPI().transform2DTranslated(self, offset).toTransform2D()
getGDNativeAPI().transform2DTranslated(self, offset)
proc xformVector2*(self: Transform2D; v: Vector2): Vector2 {.inline.} =
getGDNativeAPI().transform2DXformVector2(self, v).toVector2()
getGDNativeAPI().transform2DXformVector2(self, v)
proc xformInvVector2*(self: Transform2D; v: Vector2): Vector2 {.inline.} =
getGDNativeAPI().transform2DXformInvVector2(self, v).toVector2()
getGDNativeAPI().transform2DXformInvVector2(self, v)
proc basisXformVector2*(self: Transform2D; v: Vector2): Vector2 {.inline.} =
getGDNativeAPI().transform2DBasisXformVector2(self, v).toVector2()
getGDNativeAPI().transform2DBasisXformVector2(self, v)
proc basisXformInvVector2*(self: Transform2D; v: Vector2): Vector2 {.inline.} =
getGDNativeAPI().transform2DBasisXformInvVector2(self, v).toVector2()
getGDNativeAPI().transform2DBasisXformInvVector2(self, v)
proc xformRect2*(self: Transform2D; rect: Rect2): Rect2 {.inline.} =
getGDNativeAPI().transform2DXformRect2(self, rect).toRect2()
getGDNativeAPI().transform2DXformRect2(self, rect)
proc xformInvRect2*(self: Transform2D; rect: Rect2): Rect2 {.inline.} =
getGDNativeAPI().transform2DXformInvRect2(self, rect).toRect2()
getGDNativeAPI().transform2DXformInvRect2(self, rect)
proc interpolateWith*(self, m: Transform2D;
c: float32): Transform2D {.inline.} =
getGDNativeAPI().transform2DInterpolateWith(self, m, c).toTransform2D()
getGDNativeAPI().transform2DInterpolateWith(self, m, c)
proc `==`*(a, b: Transform2D): bool {.inline.} =
getGDNativeAPI().transform2DOperatorEqual(a, b)
proc `*`*(a, b: Transform2D): Transform2D {.inline.} =
getGDNativeAPI().transform2DOperatorMultiply(a, b).toTransform2D()
getGDNativeAPI().transform2DOperatorMultiply(a, b)
proc `*=`*(a: var Transform2D, b: Transform2D) {.inline.} =
a = a * b

View file

@ -1,10 +1,8 @@
# Copyright (c) 2018 Xored Software, Inc.
import hashes
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
import basis, vector3
import basis
proc initTransform*(): Transform {.inline.} =
result.basis = initBasis()
@ -23,54 +21,51 @@ proc initTransform*(basis: Basis, origin: Vector3): Transform {.inline.} =
proc `$`*(self: Transform): string {.inline.} =
$getGDNativeAPI().transformAsString(self)
proc hash*(self: Transform): Hash {.inline.} =
!$(self.basis.hash() !& self.origin.hash())
proc inverse*(self: Transform): Transform {.inline.} =
getGDNativeAPI().transformInverse(self).toTransform()
getGDNativeAPI().transformInverse(self)
proc affineInverse*(self: Transform): Transform {.inline.} =
getGDNativeAPI().transformAffineInverse(self).toTransform()
getGDNativeAPI().transformAffineInverse(self)
proc orthonormalized*(self: Transform): Transform {.inline.} =
getGDNativeAPI().transformOrthonormalized(self).toTransform()
getGDNativeAPI().transformOrthonormalized(self)
proc rotated*(self: Transform; axis: Vector3;
phi: float32): Transform {.inline.} =
getGDNativeAPI().transformRotated(self, axis, phi).toTransform()
getGDNativeAPI().transformRotated(self, axis, phi)
proc scaled*(self: Transform; scale: Vector3): Transform {.inline.} =
getGDNativeAPI().transformScaled(self, scale).toTransform()
getGDNativeAPI().transformScaled(self, scale)
proc translated*(self: Transform; offset: Vector3): Transform {.inline.} =
getGDNativeAPI().transformTranslated(self, offset).toTransform()
getGDNativeAPI().transformTranslated(self, offset)
proc lookingAt*(self: Transform; target, up: Vector3): Transform {.inline.} =
getGDNativeAPI().transformLookingAt(self, target, up).toTransform()
getGDNativeAPI().transformLookingAt(self, target, up)
proc xformPlane*(self: Transform; plane: Plane): Plane {.inline.} =
getGDNativeAPI().transformXformPlane(self, plane).toPlane()
getGDNativeAPI().transformXformPlane(self, plane)
proc xformInvPlane*(self: Transform; plane: Plane): Plane {.inline.} =
getGDNativeAPI().transformXformInvPlane(self, plane).toPlane()
getGDNativeAPI().transformXformInvPlane(self, plane)
proc xformVector3*(self: Transform; v: Vector3): Vector3 {.inline.} =
getGDNativeAPI().transformXformVector3(self, v).toVector3()
getGDNativeAPI().transformXformVector3(self, v)
proc xformInvVector3*(self: Transform; v: Vector3): Vector3 {.inline.} =
getGDNativeAPI().transformXformInvVector3(self, v).toVector3()
getGDNativeAPI().transformXformInvVector3(self, v)
proc xformAABB*(self: Transform; rect: AABB): AABB {.inline.} =
getGDNativeAPI().transformXformAABB(self, rect).toAABB()
getGDNativeAPI().transformXformAABB(self, rect)
proc xformInvAABB*(self: Transform; rect: AABB): AABB {.inline.} =
getGDNativeAPI().transformXformInvAABB(self, rect).toAABB()
getGDNativeAPI().transformXformInvAABB(self, rect)
proc `==`*(self: Transform; b: Transform): bool {.inline.} =
getGDNativeAPI().transformOperatorEqual(self, b)
proc `*`*(self, other: Transform): Transform {.inline.} =
getGDNativeAPI().transformOperatorMultiply(self, other).toTransform()
getGDNativeAPI().transformOperatorMultiply(self, other)
proc `*=`*(self: var Transform, other: Transform) {.inline.} =
self = self * other

View file

@ -1,9 +1,8 @@
import tables, hashes
import tables
import godotcoretypes
import internal/godotinternaltypes, internal/godotvariants,
internal/godotstrings, internal/godotdictionaries,
internal/godotarrays
import internal.godotinternaltypes, internal.godotvariants,
internal.godotstrings
type
Variant* = ref object
@ -73,10 +72,9 @@ proc newVariant*(r: cdouble): Variant {.inline.} =
proc newVariant*(s: string): Variant {.inline.} =
new(result, variantFinalizer)
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
if s.isNil:
initGodotVariant(result.godotVariant)
return
else:
var godotStr = s.toGodotString()
initGodotVariant(result.godotVariant, godotStr)
godotStr.deinit()
@ -142,7 +140,11 @@ proc newVariant*(obj: ptr GodotObject): Variant {.inline.} =
new(result, variantFinalizer)
initGodotVariant(result.godotVariant, obj)
import poolarrays
import arrays, poolarrays
proc newVariant*(arr: Array): Variant {.inline.} =
new(result, variantFinalizer)
initGodotVariant(result.godotVariant, arr.godotArray[])
proc newVariant*(pba: PoolByteArray): Variant {.inline.} =
new(result, variantFinalizer)
@ -175,119 +177,81 @@ proc newVariant*(pca: PoolColorArray): Variant {.inline.} =
proc asBool*(self: Variant): bool {.inline.} =
self.godotVariant.asBool()
proc asUInt*(self: Variant): uint64 {.inline.} =
proc asUInt*(self: Variant): uint64 =
self.godotVariant.asUInt()
proc asInt*(self: Variant): int64 {.inline.} =
proc asInt*(self: Variant): int64 =
self.godotVariant.asInt()
proc asReal*(self: Variant): cdouble {.inline.} =
proc asReal*(self: Variant): cdouble =
self.godotVariant.asReal()
proc asString*(self: Variant): string {.inline.} =
proc asString*(self: Variant): string =
$self
proc asVector2*(self: Variant): Vector2 {.inline.} =
proc asVector2*(self: Variant): Vector2 =
self.godotVariant.asVector2()
proc asRect2*(self: Variant): Rect2 {.inline.} =
proc asRect2*(self: Variant): Rect2 =
self.godotVariant.asRect2()
proc asVector3*(self: Variant): Vector3 {.inline.} =
proc asVector3*(self: Variant): Vector3 =
self.godotVariant.asVector3()
proc asTransform2D*(self: Variant): Transform2D {.inline.} =
proc asTransform2D*(self: Variant): Transform2D =
self.godotVariant.asTransform2D()
proc asPlane*(self: Variant): Plane {.inline.} =
proc asPlane*(self: Variant): Plane =
self.godotVariant.asPlane()
proc asQuat*(self: Variant): Quat {.inline.} =
proc asQuat*(self: Variant): Quat =
self.godotVariant.asQuat()
proc asAABB*(self: Variant): AABB {.inline.} =
proc asAABB*(self: Variant): AABB =
self.godotVariant.asAABB()
proc asBasis*(self: Variant): Basis {.inline.} =
proc asBasis*(self: Variant): Basis =
self.godotVariant.asBasis()
proc asTransform*(self: Variant): Transform {.inline.} =
proc asTransform*(self: Variant): Transform =
self.godotVariant.asTransform()
proc asColor*(self: Variant): Color {.inline.} =
proc asColor*(self: Variant): Color =
self.godotVariant.asColor()
proc asNodePath*(self: Variant): NodePath {.inline.} =
proc asNodePath*(self: Variant): NodePath =
result = newNodePath(self.godotVariant.asNodePath())
proc asRID*(self: Variant): RID {.inline.} =
proc asRID*(self: Variant): RID =
self.godotVariant.asRID()
proc asGodotObject*(self: Variant): ptr GodotObject {.inline.} =
proc asGodotObject*(self: Variant): ptr GodotObject =
self.godotVariant.asGodotObject()
proc asPoolByteArray*(self: Variant): PoolByteArray {.inline.} =
proc asArray*(self: Variant): Array =
newArray(self.godotVariant.asGodotArray())
proc asPoolByteArray*(self: Variant): PoolByteArray =
newPoolByteArray(self.godotVariant.asGodotPoolByteArray())
proc asPoolIntArray*(self: Variant): PoolIntArray {.inline.} =
proc asPoolIntArray*(self: Variant): PoolIntArray =
newPoolIntArray(self.godotVariant.asGodotPoolIntArray())
proc asPoolRealArray*(self: Variant): PoolRealArray {.inline.} =
proc asPoolRealArray*(self: Variant): PoolRealArray =
newPoolRealArray(self.godotVariant.asGodotPoolRealArray())
proc asPoolStringArray*(self: Variant): PoolStringArray {.inline.} =
proc asPoolStringArray*(self: Variant): PoolStringArray =
newPoolStringArray(self.godotVariant.asGodotPoolStringArray())
proc asPoolVector2Array*(self: Variant): PoolVector2Array {.inline.} =
proc asPoolVector2Array*(self: Variant): PoolVector2Array =
newPoolVector2Array(self.godotVariant.asGodotPoolVector2Array())
proc asPoolVector3Array*(self: Variant): PoolVector3Array {.inline.} =
proc asPoolVector3Array*(self: Variant): PoolVector3Array =
newPoolVector3Array(self.godotVariant.asGodotPoolVector3Array())
proc asPoolColorArray*(self: Variant): PoolColorArray {.inline.} =
proc asPoolColorArray*(self: Variant): PoolColorArray =
newPoolColorArray(self.godotVariant.asGodotPoolColorArray())
proc hash*(self: Variant): Hash =
proc objectHash(obj: ptr GodotObject): Hash =
if not obj.isNil: cast[int](obj).hash() else: 0.hash()
result = case self.getType():
of VariantType.Nil: Hash(0)
of VariantType.Bool: self.asBool().hash()
of VariantType.Int: self.asInt().hash()
of VariantType.Real: self.asReal().hash()
of VariantType.String: self.asString().hash()
of VariantType.Vector2: self.asVector2().hash()
of VariantType.Rect2: self.asRect2().hash()
of VariantType.Vector3: self.asVector3().hash()
of VariantType.Transform2D: self.asTransform2D().hash()
of VariantType.Plane: self.asPlane().hash()
of VariantType.Quat: self.asQuat().hash()
of VariantType.AABB: self.asAABB().hash()
of VariantType.Basis: self.asBasis().hash()
of VariantType.Transform: self.asTransform().hash()
of VariantType.Color: self.asColor().hash()
of VariantType.NodePath: self.asNodePath().hash()
of VariantType.RID: self.asRID().hash()
of VariantType.Object: self.asGodotObject().objectHash()
of VariantType.Dictionary: hash(self.godotVariant.asGodotDictionary().godotHash())
of VariantType.Array: hash(self.godotVariant.asGodotArray().godotHash())
of VariantType.PoolByteArray: self.asPoolByteArray().hash()
of VariantType.PoolIntArray: self.asPoolIntArray().hash()
of VariantType.PoolRealArray: self.asPoolRealArray().hash()
of VariantType.PoolStringArray: self.asPoolStringArray().hash()
of VariantType.PoolVector2Array: self.asPoolVector2Array().hash()
of VariantType.PoolVector3Array: self.asPoolVector3Array().hash()
of VariantType.PoolColorArray: self.asPoolColorArray().hash()
import arrays
proc newVariant*(arr: Array): Variant {.inline.} =
new(result, variantFinalizer)
initGodotVariant(result.godotVariant, arr.godotArray[])
proc asArray*(self: Variant): Array {.inline.} =
newArray(self.godotVariant.asGodotArray())
proc hasMethod*(self: Variant; meth: string): bool =
var s = meth.toGodotString()
result = self.godotVariant.hasMethod(s)

View file

@ -1,12 +1,10 @@
# Copyright (c) 2018 Xored Software, Inc.
import math, godotbase, hashes
import math, godotbase
import internal/godotinternaltypes, internal/godotstrings
import internal.godotinternaltypes, internal.godotstrings
import godotcoretypes, gdnativeapi
const EPSILON = 0.00001'f32
{.push stackTrace: off.}
proc vec2*(): Vector2 {.inline, noinit.} =
@ -18,9 +16,6 @@ proc vec2*(x, y: float32): Vector2 {.inline, noinit.} =
proc `$`*(self: Vector2): string {.inline, noinit.} =
$getGDNativeAPI().vector2AsString(self)
proc hash*(self: Vector2): Hash {.inline, noinit.} =
!$(self.x.hash() !& self.y.hash())
proc `+`*(self, other: Vector2): Vector2 {.inline, noinit.} =
Vector2(x: self.x + other.x, y: self.y + other.y)
@ -89,7 +84,7 @@ proc length*(self: Vector2): float32 {.inline, noinit.} =
proc lengthSquared*(self: Vector2): float32 {.inline, noinit.} =
self.x * self.x + self.y * self.y
proc normalize*(self: var Vector2) {.inline.} =
proc normalize(self: var Vector2) {.inline.} =
var len = self.x * self.x + self.y * self.y
if len != 0:
len = sqrt(len)
@ -201,14 +196,4 @@ proc clamped*(self: Vector2; length: float32): Vector2 {.noinit.} =
result /= len
result *= length
proc moveToward*(vFrom, to: Vector2, delta: float32): Vector2 =
let
vd = to - vFrom
vLen = vd.length
if vLen <= delta or vLen < EPSILON:
result = to
else:
result = vFrom + (vd / vLen) * delta
{.pop.} # stackTrace: off

View file

@ -1,10 +1,8 @@
# Copyright (c) 2018 Xored Software, Inc.
import math, hashes
import math
import godotbase, godotcoretypes
const EPSILON = 0.00001'f32
{.push stackTrace: off.}
proc vec3*(): Vector3 {.inline.} =
@ -23,9 +21,6 @@ proc `$`*(self: Vector3): string {.inline.} =
result.add($self.z)
result.add(')')
proc hash*(self: Vector3): Hash {.inline, noinit.} =
!$(self.x.hash() !& self.y.hash() !& self.z.hash())
proc `+`*(a, b: Vector3): Vector3 {.inline.} =
result.x = a.x + b.x
result.y = a.y + b.y
@ -241,14 +236,4 @@ proc cubicInterpolate*(self, b, preA, postB: Vector3;
(2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
proc moveToward*(vFrom, to: Vector3, delta: float32): Vector3 =
let
vd = to - vFrom
vLen = vd.length
if vLen <= delta or vLen < EPSILON:
result = to
else:
result = vFrom + (vd / vLen) * delta
{.pop.} # stackTrace: off

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,14 @@
# Copyright 2018 Xored Software, Inc.
import core/godotbase, godotinternal
import core/godotcoretypes
import core/vector2, core/rect2, core/vector3, core/transform2d, core/planes,
core/quats
import core/aabb, core/basis, core/transforms, core/colors, core/nodepaths,
core/rids
import core/dictionaries, core/arrays, core/poolarrays, core/variants
import core.godotbase, godotinternal
import core.godotcoretypes
import core.vector2, core.rect2, core.vector3, core.transform2d, core.planes,
core.quats
import core.aabb, core.basis, core.transforms, core.colors, core.nodepaths,
core.rids
import core.dictionaries, core.arrays, core.poolarrays, core.variants
import nim/godotmacros, nim/godotnim
import nim.godotmacros, nim.godotnim
export godotbase, godotcoretypes, vector2, rect2, vector3, transform2d, planes,
quats, aabb, basis, transforms, colors, nodepaths, rids, dictionaries,

View file

@ -1,31 +1,12 @@
# Copyright 2018 Xored Software, Inc.
import streams, json, os, strutils, times, sets, tables, options
import streams, json, os, strutils, times, sets, tables
import sequtils, algorithm
import compiler/ast, compiler/renderer, compiler/idents, compiler/astalgo
include "internal/backwardcompat.inc.nim"
when (NimMajor, NimMinor, NimPatch) >= (0, 19, 0):
import compiler/lineinfos
when (NimMajor, NimMinor, NimPatch) >= (0, 19, 0):
var gic = newIdentCache()
proc getIdent(ident: string): PIdent =
getIdent(gic, ident)
import compiler.ast, compiler.renderer, compiler.idents, compiler.astalgo
proc ident(ident: string): PNode =
result = PNode(kind: nkIdent, ident: getIdent(ident))
proc newPNode(kind: TNodeKind): PNode =
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
result = newNode(kind)
else:
result = PNode(kind: kind)
result.info.fileIndex = InvalidFileIdx
result.info.col = int16(-1)
result.info.line = uint16(0)
result = newNode(nkIdent)
result.ident = getIdent(ident)
proc addChain(node: PNode, others: varargs[PNode]): PNode {.discardable.} =
for other in others:
@ -34,37 +15,37 @@ proc addChain(node: PNode, others: varargs[PNode]): PNode {.discardable.} =
proc newIdentDefs(name, typ: PNode,
defaultVal: PNode = nil): PNode =
result = newPNode(nkIdentDefs)
result = newNode(nkIdentDefs)
result.add(name)
result.add(typ)
if defaultVal.isNil:
result.add(newPNode(nkEmpty))
result.add(newNode(nkEmpty))
else:
result.add(defaultVal)
proc newProc(name: PNode, params: openArray[PNode], body = newPNode(nkEmpty),
proc newProc(name: PNode, params: openArray[PNode], body = newNode(nkEmpty),
procType = nkProcDef): PNode =
result = newPNode(procType)
result = newNode(procType)
result.add(name)
result.add(newPNode(nkEmpty))
result.add(newPNode(nkEmpty))
let formalParams = newPNode(nkFormalParams)
result.add(newNode(nkEmpty))
result.add(newNode(nkEmpty))
let formalParams = newNode(nkFormalParams)
for param in params:
formalParams.add(param)
result.add(formalParams)
result.add(newPNode(nkEmpty))
result.add(newPNode(nkEmpty))
result.add(newNode(nkEmpty))
result.add(newNode(nkEmpty))
result.add(body)
proc newIfStmt(condition, body: PNode): PNode =
result = newPNode(nkIfStmt)
let branch = newPNode(nkElifBranch)
result = newNode(nkIfStmt)
let branch = newNode(nkElifBranch)
result.add(branch)
branch.add(condition)
branch.add(body)
proc newCall(theProc: PNode, args: varargs[PNode]): PNode =
result = newPNode(nkCall)
result = newNode(nkCall)
result.add(theProc)
for arg in args:
result.add(arg)
@ -73,66 +54,66 @@ proc newCall(theProc: string, args: varargs[PNode]): PNode =
newCall(ident(theProc), args)
proc newStrLit(s: string): PNode =
result = newPNode(nkStrLit)
result = newNode(nkStrLit)
result.strVal = s
proc newRStrLit(s: string): PNode =
result = newPNode(nkRStrLit)
result = newNode(nkRStrLit)
result.strVal = s
proc newCStringLit(s: string): PNode =
newPNode(nkCallStrLit).addChain(ident("cstring"), newRStrLit(s))
newNode(nkCallStrLit).addChain(ident("cstring"), newRStrLit(s))
proc newIntLit(val: BiggestInt): PNode =
result = newPNode(nkIntLit)
result = newNode(nkIntLit)
result.intVal = val
proc newInt64Lit(val: BiggestInt): PNode =
result = newPNode(nkInt64Lit)
result = newNode(nkInt64Lit)
result.intVal = val
proc newFloatLit(val: BiggestFloat): PNode =
result = newPNode(nkFloatLit)
result = newNode(nkFloatLit)
result.floatVal = val
proc newFloat64Lit(val: BiggestFloat): PNode =
result = newPNode(nkFloat64Lit)
result = newNode(nkFloat64Lit)
result.floatVal = val
proc newNilLit(): PNode =
result = newPNode(nkNilLit)
result = newNode(nkNilLit)
proc newDotExpr(left, right: PNode): PNode =
result = newPNode(nkDotExpr)
result = newNode(nkDotExpr)
result.add(left)
result.add(right)
proc newCommand(left, right: PNode): PNode =
result = newPNode(nkCommand)
result = newNode(nkCommand)
result.add(left)
result.add(right)
proc infix(left, op, right: PNode): PNode =
result = newPNode(nkInfix)
result = newNode(nkInfix)
result.add(op)
result.add(left)
result.add(right)
proc postfix(left: PNode, op: string): PNode =
result = newPNode(nkPostfix)
result = newNode(nkPostfix)
result.add(ident(op))
result.add(left)
proc prefix(op: string, right: PNode): PNode =
result = newPNode(nkPrefix)
result = newNode(nkPrefix)
result.add(ident(op))
result.add(right)
proc newBracketExpr(first, bracket: PNode): PNode =
newPNode(nkBracketExpr).addChain(first, bracket)
newNode(nkBracketExpr).addChain(first, bracket)
proc newEmptyNode(): PNode =
newPNode(nkEmpty)
newNode(nkEmpty)
type GodotType = ref object
name: string
@ -158,19 +139,19 @@ proc findCommonRoot(typeRegistry: Table[string, GodotType],
var idx = 0
while true:
var curType = none(string)
var curType: string
var allEqual = true
for chain in chains:
if idx >= chain.len:
allEqual = false
break
if curType.isNone:
curType = some(chain[idx])
elif curType.get != chain[idx]:
if curType.isNil:
curType = chain[idx]
elif curType != chain[idx]:
allEqual = false
break
if allEqual:
result = curType.get
result = curType
else:
break
inc idx
@ -225,42 +206,42 @@ proc toNimStyle(name: string): string =
proc newRefTypeNode(typeSection: PNode, typ, base, doc: string,
isExported = true) =
let typeDef = newPNode(nkTypeDef)
let typeDef = newNode(nkTypeDef)
typeSection.add(typeDef)
if isExported:
typeDef.add(postfix(ident(typ), "*"))
else:
typeDef.add(ident(typ))
typeDef.add(newPNode(nkEmpty)) # generic
let objTy = newPNode(nkObjectTy)
let refTy = newPNode(nkRefTy)
typeDef.add(newNode(nkEmpty)) # generic
let objTy = newNode(nkObjectTy)
let refTy = newNode(nkRefTy)
refTy.add(objTy)
typeDef.add(refTy)
objTy.add(newPNode(nkEmpty))
let inherit = newPNode(nkOfInherit)
objTy.add(newNode(nkEmpty))
let inherit = newNode(nkOfInherit)
inherit.add(ident(base))
objTy.add(inherit)
if doc.len > 0:
let recList = newPNode(nkRecList)
let docNode = newPNode(nkCommentStmt)
if not doc.isNil:
let recList = newNode(nkRecList)
let docNode = newNode(nkCommentStmt)
docNode.comment = doc
recList.add(docNode)
objTy.add(recList)
else:
objTy.add(newPNode(nkEmpty))
objTy.add(newNode(nkEmpty))
proc makeConstSection(constObj: JsonNode): PNode =
if constObj.len == 0:
return newPNode(nkEmpty)
return newNode(nkEmpty)
result = newPNode(nkConstSection)
result = newNode(nkConstSection)
for field, val in constObj:
let def = newPNode(nkConstDef)
let def = newNode(nkConstDef)
def.add(postfix(ident(field), "*"))
def.add(newPNode(nkEmpty)) # infer type
def.add(newNode(nkEmpty)) # infer type
var valNode: PNode
if val.kind == JInt:
valNode = newInt64Lit(val.num)
@ -270,7 +251,7 @@ proc makeConstSection(constObj: JsonNode): PNode =
elif val.kind == JBool:
valNode = ident($val.bval)
elif val.kind == JString:
valNode = newPNode(nkStrLit)
valNode = newNode(nkStrLit)
valNode.strVal = val.str
else:
raise newException(ValueError, "Unexpected constant kind: " & $val.kind)
@ -294,47 +275,40 @@ type
godotName: string
typ: GodotType
args: seq[MethodArg]
returnType: Option[string]
returnType: string
isVirtual: bool
isBase: bool
isDiscardable: bool
const standardTypes = toHashSet(
const standardTypes = toSet(
["bool", "cint", "int", "uint8", "int8", "uint16", "int16", "uint32", "int32",
"uint64", "Error",
"uint64",
"int64", "float32", "cfloat", "float64", "GodotString", "Vector2", "Rect2",
"Vector3", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
"Color", "RID", "NodePath", "Dictionary", "Variant", "Array",
"PoolByteArray", "PoolIntArray", "PoolRealArray", "PoolStringArray",
"PoolVector2Array", "PoolVector3Array", "PoolColorArray"])
const variantPrimitives = [
"bool", "cint", "int", "uint8", "int8", "uint16", "int16", "uint32", "int32",
"uint64", "Error",
"int64", "float32", "cfloat", "float64", "GodotString", "Vector2", "Rect2",
"Vector3", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
"Color", "RID"]
const smallIntTypes = toHashSet(["uint8", "int8", "uint16", "int16", "uint32",
const smallIntTypes = toSet(["uint8", "int8", "uint16", "int16", "uint32",
"int32", "cint", "int", "Error"])
const int64Types = toHashSet(["uint64", "int64"])
const int64Types = toSet(["uint64", "int64"])
const intTypes = union(smallIntTypes, int64Types)
const float64Types = toHashSet(["float64", "cdouble"])
const float32Types = toHashSet(["float32", "cfloat"])
const float64Types = toSet(["float64", "cdouble"])
const float32Types = toSet(["float32", "cfloat"])
const floatTypes = union(float64Types, float32Types)
const arrayTypes = toHashSet(["Array", "PoolByteArray",
const arrayTypes = toSet(["Array", "PoolByteArray",
"PoolIntArray", "PoolRealArray", "PoolStringArray", "PoolVector2Array",
"PoolVector3Array", "PoolColorArray"])
const wrapperTypes = union(arrayTypes,
toHashSet(["NodePath", "Dictionary", "Variant"]))
toSet(["NodePath", "Dictionary", "Variant"]))
proc getInternalPtr(varName: PNode, typ: string): PNode =
assert(typ in wrapperTypes or typ == "Object")
assert(typ in wrapperTypes)
result = newDotExpr(varName, ident("godot" & typ))
proc newNilCheck(ident, toAssign: PNode): PNode =
newIfStmt(newCall("isNil", ident),
newPNode(nkAsgn).addChain(ident).addChain(
newNode(nkAsgn).addChain(ident).addChain(
toAssign
)
)
@ -345,7 +319,6 @@ proc singletonName(typ: string): PNode =
proc makeDefaultValue(arg: MethodArg): PNode =
result = newEmptyNode()
if not arg.defaultVal.isNil:
var failed = false
if arg.typ == "bool":
result = ident(arg.defaultVal.str.toLowerAscii())
elif arg.typ in intTypes:
@ -362,15 +335,15 @@ proc makeDefaultValue(arg: MethodArg): PNode =
elif arg.typ == "Color":
result = newCall("initColor",
arg.defaultVal.str.split(',').mapIt(newFloatLit(parseFloat(it))))
# elif arg.typ in arrayTypes and (arg.defaultVal.str == "[]" or
# arg.defaultVal.str == "[" & arg.typ & "]"):
# result = newCall("init" & arg.typ)
elif arg.typ == "RID" and arg.defaultVal.str == "[RID]":
result = newCall("initRID")
elif arg.typ in arrayTypes and (arg.defaultVal.str == "[]" or
arg.defaultVal.str == "[" & arg.typ & "]"):
result = newCall("new" & arg.typ)
elif arg.typ == "Vector2" or arg.typ == "Vector3" or arg.typ == "Rect2":
let parts = arg.defaultVal.str.replace("(", "").replace(")", "").
replace(" ", "").
split(',').mapIt(newFloatLit(parseFloat(it)))
split(",").mapIt(newFloatLit(parseFloat(it)))
if arg.typ.endsWith("r2"):
result = newCall("vec2", parts)
elif arg.typ.endsWith('3'):
@ -379,43 +352,11 @@ proc makeDefaultValue(arg: MethodArg): PNode =
result = newCall("initRect2", parts)
elif arg.defaultVal.str == "[Object:null]":
result = newNilLit()
elif arg.typ == "Transform":
let transformParts = arg.defaultVal.str.replace(" ", "").split('-')
doAssert(transformParts.len == 2)
let basisParts = transformParts[0].split(',').mapIt(
newFloatLit(parseFloat(it)))
let originParts = transformParts[1].split(',').mapIt(
newFloatLit(parseFloat(it)))
let basis = newCall("initBasis", basisParts)
let origin = newCall("vec3", originParts)
result = newCall("initTransform", basis, origin)
elif arg.typ == "Transform2D":
let parts = arg.defaultVal.str.
replace(" ", "").
replace(")", "").
replace("(", "").
split(',').mapIt(newFloatLit(parseFloat(it)))
doAssert(parts.len == 6)
let xAxis = newCall("vec2", parts[0..1])
let yAxis = newCall("vec2", parts[2..3])
let origin = newCall("vec2", parts[4..5])
result = newCall("initTransform2D", xAxis, yAxis, origin)
elif arg.typ == "Variant":
if arg.defaultVal.str == "0":
result = newCall("newVariant", newIntLit(0))
elif arg.defaultVal.str == "Null":
result = newCall("newVariant")
else:
failed = true
elif arg.typ == "Dictionary":
result = newCall("newDictionary")
else:
failed = true
if failed:
elif arg.typ != "Transform" and arg.typ != "Transform2D" and
arg.typ != "Variant" and arg.typ notin arrayTypes: # TODO
raise newException(
ValueError,
"Cannot build default value from \"$#\" ($#) - please report this at https://github.com/pragmagic/godot-nim/issues" %
"Cannot build default value from $# ($#)" %
[$arg.defaultVal, arg.typ])
proc boundArgName(idx: int): string =
@ -425,12 +366,12 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
meth: MethodInfo, withImplementation: bool) =
var body: PNode
if not withImplementation:
body = newPNode(nkEmpty)
body = newNode(nkEmpty)
elif meth.godotName == "_init" and meth.typ.godotName == "Object":
# It's not real (calling it crashes) and will be removed later,
# but we use init() convention in Nim, so it needs to work.
body = newPNode(nkStmtList).addChain(
newPNode(nkDiscardStmt).addChain(newPNode(nkEmpty)))
body = newNode(nkStmtList).addChain(
newNode(nkDiscardStmt).addChain(newNode(nkEmpty)))
else:
let methGodotName = if meth.godotName.startsWith('_'):
"underscore" & meth.godotName[1..^1]
@ -441,12 +382,12 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
if methodBindName[0].isUpperAscii():
methodBindName[0] = methodBindName[0].toLowerAscii()
if methodBindName notin methodBindRegistry:
let methodBindDecl = newPNode(nkVarSection).addChain(
let methodBindDecl = newNode(nkVarSection).addChain(
newIdentDefs(
newPNode(nkPragmaExpr).addChain(
newNode(nkPragmaExpr).addChain(
ident(methodBindName)).addChain(
newPNode(nkPragma).addChain(ident("threadvar"))),
newPNode(nkPtrTy).addChain(ident("GodotMethodBind"))
newNode(nkPragma).addChain(ident("threadvar"))),
newNode(nkPtrTy).addChain(ident("GodotMethodBind"))
))
tree.add(methodBindDecl)
methodBindRegistry.incl(methodBindName)
@ -455,8 +396,8 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
newCall("getMethod", newCStringLit(meth.typ.godotName),
newCStringLit(meth.godotName))
)
let vars = newPNode(nkVarSection)
var varargsName = none(string)
let vars = newNode(nkVarSection)
var varargsName: string
var isVarargs: bool
var staticArgsLen: int
var argLenNode = newIntLit(0)
@ -464,90 +405,54 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
if meth.args.len > 0:
for idx, arg in meth.args:
if arg.kind == ArgKind.VarArgs:
varargsName = some(arg.name)
varargsName = arg.name
isVarargs = true
vars.add(newIdentDefs(ident("callError"), ident("VariantCallError")))
elif arg.kind == ArgKind.Bound:
let argName = boundArgName(idx)
vars.add(newIdentDefs(
ident(argName), newPNode(nkEmpty), arg.makeDefaultValue()))
ident(argName), newNode(nkEmpty), arg.makeDefaultValue()))
let argsAlloc = newPNode(nkCast).addChain(
newPNode(nkPtrTy).addChain(
newPNode(nkBracketExpr).addChain(
let argsAlloc = newNode(nkCast).addChain(
newNode(nkPtrTy).addChain(
newNode(nkBracketExpr).addChain(
ident("array"), ident("MAX_ARG_COUNT"),
if isVarargs: newPNode(nkPtrTy).addChain(ident("GodotVariant"))
if isVarargs: newNode(nkPtrTy).addChain(ident("GodotVariant"))
else: ident("pointer"))))
staticArgsLen = if varargsName.isNone: meth.args.len
staticArgsLen = if varargsName.isNil: meth.args.len
else: meth.args.len - 1
if varargsName.isSome:
if not varargsName.isNil:
argLenNode = newCall("cint",
infix(newIntLit(staticArgsLen),
ident("+"),
newDotExpr(ident(varargsName.get), ident("len"))))
newDotExpr(ident(varargsName), ident("len"))))
argsAlloc.add(newCall("godotAlloc", newCall("cint", infix(
newCall("sizeof", newPNode(nkPtrTy).addChain(ident("GodotVariant"))), ident("*"),
newPNode(nkPar).addChain(argLenNode))))
newCall("sizeof", ident("Variant")), ident("*"),
newNode(nkPar).addChain(argLenNode))))
)
else:
argLenNode = newCall("cint", newIntLit(staticArgsLen))
vars.add(newIdentDefs(
ident("argsStatic"),
newPNode(nkBracketExpr).addChain(
newNode(nkBracketExpr).addChain(
ident("array"), newIntLit(staticArgsLen), ident("pointer")),
newPNode(nkEmpty)))
newNode(nkEmpty)))
argsAlloc.add(newCommand(ident("addr"), ident("argsStatic")))
vars.add(newIdentDefs(argsName, newPNode(nkEmpty), argsAlloc))
vars.add(newIdentDefs(argsName, newNode(nkEmpty), argsAlloc))
let argConversions = newPNode(nkStmtList)
let destructors = newPNode(nkStmtList)
let argConversions = newNode(nkStmtList)
for idx, arg in meth.args:
var argName = if arg.kind == ArgKind.Bound: ident(boundArgName(idx))
else: ident(arg.name)
if arg.kind == ArgKind.VarArgs:
argName = newPNode(nkBracketExpr).addChain(
ident(varargsName.get),
argName = newNode(nkBracketExpr).addChain(
ident(varargsName),
infix(ident("idx"), ident("-"), newIntLit(staticArgsLen)))
let argIdx = if arg.kind == ArgKind.VarArgs: ident("idx") else: newIntLit(idx)
let isStandardType = arg.typ in standardTypes
let isWrappedType = arg.typ in wrapperTypes
let convArg =
if varargsName.isSome:
let isAlreadyVariant = arg.typ == "Variant"
let variantIdent = if isAlreadyVariant: getInternalPtr(argName, "Variant")
else: ident("variant" & $idx)
if not isAlreadyVariant:
argConversions.add(newPNode(nkVarSection).addChain(
newIdentDefs(variantIdent, ident("GodotVariant"), newEmptyNode())
))
destructors.add(
newCall("deinit", variantIdent)
)
if arg.typ in variantPrimitives:
argConversions.add(
newCall("initGodotVariant", variantIdent, argName)
)
elif arg.typ in wrapperTypes or arg.typ == "Object":
argConversions.add(
newCall("initGodotVariant", variantIdent, getInternalPtr(argName, arg.typ))
)
elif arg.typ == "string":
let stringIdent = ident("variantString" & $idx)
argConversions.addChain(
newPNode(nkVarSection).addChain(
newIdentDefs(stringIdent, ident("GodotString"), newCall("toGodotString", argName))
)
).addChain(
newCall("initGodotVariant", variantIdent, stringIdent)
)
destructors.add(newCall("deinit", stringIdent))
else:
raise newException(ValueError,
"Non-standard type $# for varargs params of method $# of type $#" %
[arg.typ, meth.godotName, meth.typ.godotName])
if isAlreadyVariant: variantIdent
else: newCall("addr", variantIdent)
let convArg = if not varargsName.isNil:
getInternalPtr(newCall("toVariant", argName), "Variant")
elif isWrappedType: getInternalPtr(argName, arg.typ)
elif isStandardType: newCall("unsafeAddr", argName)
elif arg.typ == "string": newCall("unsafeAddr",
@ -557,21 +462,18 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
raise newException(ValueError,
"Non-standard type $# for varargs params of method $# of type $#" %
[arg.typ, meth.godotName, meth.typ.godotName])
if not isStandardType and varargsName.isNone:
if not isStandardType and varargsName.isNil:
if arg.typ == "string":
argConversions.add(newPNode(nkVarSection).addChain(
argConversions.add(newNode(nkVarSection).addChain(
newIdentDefs(ident("argToPassToGodot" & $idx), newEmptyNode(),
newCall("toGodotString", argName))))
else:
let objVal = newIfStmt(
prefix("not", newDotExpr(argName, ident"isNil")),
newDotExpr(argName, ident("godotObject"))
).addChain(newPNode(nkElse).addChain(newNilLit()))
argConversions.add(newPNode(nkLetSection).addChain(
newIdentDefs(ident("argToPassToGodot" & $idx), newEmptyNode(), objVal)))
let argAsgn = newPNode(nkAsgn).addChain(
newPNode(nkBracketExpr).addChain(
newPNode(nkBracketExpr).addChain(argsName), argIdx),
argConversions.add(newNode(nkLetSection).addChain(
newIdentDefs(ident("argToPassToGodot" & $idx), newEmptyNode(),
newDotExpr(argName, ident("godotObject")))))
let argAsgn = newNode(nkAsgn).addChain(
newNode(nkBracketExpr).addChain(
newNode(nkBracketExpr).addChain(argsName), argIdx),
convArg
)
if arg.kind != ArgKind.VarArgs:
@ -579,14 +481,14 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
argAsgn
)
else:
argConversions.add(newPNode(nkVarSection).addChain(
newIdentDefs(ident("idx"), newPNode(nkEmpty),
argConversions.add(newNode(nkVarSection).addChain(
newIdentDefs(ident("idx"), newNode(nkEmpty),
newIntLit(staticArgsLen))))
let argLoop = newPNode(nkWhileStmt)
let argLoop = newNode(nkWhileStmt)
argConversions.add(argLoop)
argLoop.add(infix(ident("idx"), ident("<"), argLenNode)
)
argLoop.add(newPNode(nkStmtList).addChain(
argLoop.add(newNode(nkStmtList).addChain(
argAsgn,
newCommand(ident("inc"), ident("idx"))))
@ -595,26 +497,26 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
newDotExpr(ident(methodBindName),
ident(if isVarargs: "call" else: "ptrCall")),
newDotExpr(ident("self"), ident("godotObject")),
if meth.args.len > 0: argsName else: newPNode(nkNilLit),
if meth.args.len > 0: argsName else: newNode(nkNilLit),
argLenNode,
if not isVarargs: ident(retName) else: ident("callError")
)
if not isVarargs:
theCall.sons.delete(3)
let freeCall = newCall("godotFree", argsName)
body = newPNode(nkStmtList)
body = newNode(nkStmtList)
if meth.typ.isSingleton:
let varName = singletonName(meth.typ.name)
body.add(newNilCheck(varName, newCall(
newBracketExpr(ident("getSingleton"), ident(meth.typ.name)))))
body.add(newPNode(nkLetSection).addChain(newIdentDefs(
ident("self"), newPNode(nkEmpty), varName
body.add(newNode(nkLetSection).addChain(newIdentDefs(
ident("self"), newNode(nkEmpty), varName
)))
body.add(nilCheck)
body.add(vars)
body.add(argConversions)
if not isVarargs:
let retPtrDecl = newPNode(nkVarSection).addChain(
let retPtrDecl = newNode(nkVarSection).addChain(
newIdentDefs(ident(retName), ident("pointer"), newEmptyNode())
)
body.add(retPtrDecl)
@ -624,109 +526,105 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
var isStringRet: bool
var isWrapperRet: bool
let retValIdent = if not isVarargs: ident("ptrCallVal") else: ident(retName)
if meth.returnType.isSome and not isVarargs:
let returnType = meth.returnType.get
if not meth.returnType.isNil and not isVarargs:
var addrToAssign = newCall("addr", retValIdent)
if returnType in smallIntTypes:
if meth.returnType in smallIntTypes:
isConversionRet = true
body.add(newPNode(nkVarSection).addChain(newIdentDefs(
body.add(newNode(nkVarSection).addChain(newIdentDefs(
retValIdent, ident("int64"),
)))
elif returnType in float32Types:
elif meth.returnType in float32Types:
isConversionRet = true
body.add(newPNode(nkVarSection).addChain(newIdentDefs(
body.add(newNode(nkVarSection).addChain(newIdentDefs(
retValIdent, ident("float64"),
)))
elif returnType in wrapperTypes:
elif meth.returnType in wrapperTypes:
isWrapperRet = true
body.add(newPNode(nkVarSection).addChain(newIdentDefs(
retValIdent, ident("Godot" & returnType),
body.add(newNode(nkVarSection).addChain(newIdentDefs(
retValIdent, ident("Godot" & meth.returnType),
)))
if returnType != "NodePath":
body.add(newCall("initGodot" & returnType, retValIdent))
elif returnType in standardTypes:
elif meth.returnType in standardTypes:
addrToAssign = newCall("addr", ident("result"))
elif returnType == "string":
elif meth.returnType == "string":
isStringRet = true
body.add(newPNode(nkVarSection).addChain(newIdentDefs(
body.add(newNode(nkVarSection).addChain(newIdentDefs(
retValIdent, ident("GodotString"),
)))
elif returnType != "void":
elif meth.returnType != "void":
isObjRet = true
body.add(newPNode(nkVarSection).addChain(newIdentDefs(
retValIdent, newPNode(nkPtrTy).addChain(ident("GodotObject")),
body.add(newNode(nkVarSection).addChain(newIdentDefs(
retValIdent, newNode(nkPtrTy).addChain(ident("GodotObject")),
)))
body.add(newPNode(nkAsgn).addChain(ident(retName), addrToAssign))
body.add(newNode(nkAsgn).addChain(ident(retName), addrToAssign))
if not isVarargs:
body.add(theCall)
else:
if meth.returnType.isSome:
if not meth.returnType.isNil:
isVariantRet = true
let callStmt = newPNode(nkLetSection).addChain(
let callStmt = newNode(nkLetSection).addChain(
newIdentDefs(
newPNode(nkPragmaExpr).addChain(
ident(retName), newPNode(nkPragma).addChain(ident("used"))),
newPNode(nkEmpty), theCall))
newNode(nkPragmaExpr).addChain(
ident(retName), newNode(nkPragma).addChain(ident("used"))),
newNode(nkEmpty), theCall))
body.add(callStmt)
if varargsName.isNone:
if varargsName.isNil:
for idx, arg in meth.args:
if arg.typ == "string":
body.add(newCall("deinit", ident("argToPassToGodot" & $idx)))
if varargsName.isSome:
if meth.args.len > 0:
if meth.args.len > 0 and not varargsName.isNil:
body.add(freeCall)
body.add(destructors)
if not varargsName.isNil:
let errCheck = newIfStmt(
infix(newDotExpr(ident("callError"), ident("error")), ident("!="),
newDotExpr(ident("VariantCallErrorType"), ident("OK"))),
newPNode(nkRaiseStmt).addChain(
newNode(nkRaiseStmt).addChain(
newCall("newCallError", ident("callError")))
)
body.add(errCheck)
if isVariantRet:
let convErrDef = newPNode(nkLetSection).addChain(
newIdentDefs(ident("convErr"), newPNode(nkEmpty),
let convErrDef = newNode(nkLetSection).addChain(
newIdentDefs(ident("convErr"), newNode(nkEmpty),
newCall("fromVariant", ident("result"),
newCall("newVariant", retValIdent))))
let convCheck = newIfStmt(
infix(ident("convErr"), ident("!="),
newDotExpr(ident("ConversionResult"), ident("OK"))),
newPNode(nkRaiseStmt).addChain(
newNode(nkRaiseStmt).addChain(
newCall("newConversionError", ident("convErr")))
)
body.add(convErrDef)
body.add(convCheck)
if isStringRet:
body.add(newPNode(nkAsgn).addChain(
body.add(newNode(nkAsgn).addChain(
ident("result"), prefix("$", retValIdent)))
body.add(newCall("deinit", retValIdent))
elif isConversionRet:
body.add(newPNode(nkAsgn).addChain(
body.add(newNode(nkAsgn).addChain(
ident("result"),
newCall(meth.returnType.get, retValIdent)))
newCall(meth.returnType, retValIdent)))
elif isWrapperRet:
body.add(newPNode(nkAsgn).addChain(
body.add(newNode(nkAsgn).addChain(
ident("result"),
newCall("new" & meth.returnType.get, retValIdent)))
newCall("new" & meth.returnType, retValIdent)))
elif isObjRet:
body.add(newPNode(nkAsgn).addChain(ident("result"),
body.add(newNode(nkAsgn).addChain(ident("result"),
newCall(newBracketExpr(ident("asNimGodotObject"),
newCall("type", ident("result"))),
retValIdent, ident("false"), ident("true"))))
elif isStringRet:
body.add(newPNode(nkAsgn).addChain(ident("result"),
body.add(newNode(nkAsgn).addChain(ident("result"),
newCall("$", retValIdent)))
let procType = if meth.isVirtual: nkMethodDef
else: nkProcDef
var params = newSeqOfCap[PNode](meth.args.len + 2)
if meth.returnType.isSome:
params.add(ident(meth.returnType.get))
if not meth.returnType.isNil:
params.add(ident(meth.returnType))
else:
params.add(newPNode(nkEmpty))
params.add(newNode(nkEmpty))
if not meth.typ.isSingleton:
params.add(newIdentDefs(ident("self"), ident(toNimType(meth.typ.name))))
@ -737,7 +635,7 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
params.add(newIdentDefs(ident(arg.name), ident(arg.typ), defaultValue))
of ArgKind.VarArgs:
params.add(newIdentDefs(ident(arg.name),
newPNode(nkBracketExpr).addChain(ident("varargs"), ident(arg.typ))))
newNode(nkBracketExpr).addChain(ident("varargs"), ident(arg.typ))))
of ArgKind.Bound:
discard
@ -745,14 +643,14 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
else: meth.name,
params, body, procType)
if not withImplementation:
let pragma = newPNode(nkPragma)
let pragma = newNode(nkPragma)
pragma.add(ident("gcsafe"))
pragma.add(newPNode(nkExprColonExpr).addChain(ident("locks"),
pragma.add(newNode(nkExprColonExpr).addChain(ident("locks"),
newIntLit(0)))
if meth.isVirtual and meth.isBase and meth.typ.name != "PhysicsBody":
# Nim doesn't like `base` on PhysicsBody methods - wtf
pragma.add(ident("base"))
if meth.isDiscardable and meth.returnType.isSome:
if meth.isDiscardable and not meth.returnType.isNil:
pragma.add(ident("discardable"))
procDecl.sons[4] = pragma
tree.add(procDecl)
@ -797,9 +695,9 @@ proc getMethodInfo(methodObj: JsonNode, types: Table[string, GodotType],
nimName = nimName & "Impl"
let returnType = if methodObj["return_type"].str != "void":
some(toNimType(types, methodObj["return_type"].str))
else: none(string)
const discardableMethods = toHashSet(["emit_signal"])
toNimType(types, methodObj["return_type"].str)
else: nil
const discardableMethods = toSet(["emit_signal"])
result = MethodInfo(
name: ident(nimName),
typ: typ,
@ -821,24 +719,19 @@ proc makeProperty(types: Table[string, GodotType], tree: PNode,
methodBindRegistry: var HashSet[string],
typ: GodotType, propertyObj: JsonNode, obj: JsonNode,
withImplementation: bool) =
for meth in obj["methods"]:
if meth["name"].str == '_' & propertyObj["name"].str:
# Don't generate properties if there is a name clash - otherwise
# using the property would not compile due to ambiguity.
return
let getterNameStr = toNimStyle(propertyObj["name"].str)
let getterName = ident(getterNameStr)
let setterName = newPNode(nkAccQuoted).addChain(ident(getterNameStr & "="))
let setterName = newNode(nkAccQuoted).addChain(ident(getterNameStr & "="))
let getter = findMethod(obj, propertyObj["getter"].str)
let nimType = if getter.isNil: toNimType(types, propertyObj["type"].str)
else: getMethodInfo(getter, types, typ).returnType.get("void")
else: getMethodInfo(getter, types, typ).returnType
let getterInfo = MethodInfo(
name: getterName,
typ: typ,
godotName: propertyObj["getter"].str,
args: newSeq[MethodArg](),
returnType: some(nimType)
args: @[],
returnType: nimType
)
let setterInfo = MethodInfo(
name: setterName,
@ -847,11 +740,9 @@ proc makeProperty(types: Table[string, GodotType], tree: PNode,
args: @[MethodArg(name: "val", typ: nimType)]
)
if "index" in propertyObj and propertyObj["index"] != newJInt(-1):
getterInfo.args =
@[MethodArg(defaultVal: newJString($propertyObj["index"]),
getterInfo.args = @[MethodArg(defaultVal: newJString($propertyObj["index"]),
typ: "int", kind: ArgKind.Bound)] & getterInfo.args
setterInfo.args =
@[MethodArg(defaultVal: newJString($propertyObj["index"]),
setterInfo.args = @[MethodArg(defaultVal: newJString($propertyObj["index"]),
typ: "int", kind: ArgKind.Bound)] & setterInfo.args
generateMethod(tree, methodBindRegistry, getterInfo, withImplementation)
generateMethod(tree, methodBindRegistry, setterInfo, withImplementation)
@ -867,12 +758,6 @@ proc makeMethod(types: Table[string, GodotType], tree: PNode,
for prop in typ.jsonNode["properties"]:
if prop["getter"].str == methodObj["name"].str or
prop["setter"].str == methodObj["name"].str:
var hasDuplicateMethod = false
for meth in typ.jsonNode["methods"]:
if meth["name"].str == '_' & prop["name"].str:
hasDuplicateMethod = true
break
if not hasDuplicateMethod:
return
let methodInfo = getMethodInfo(methodObj, types, typ)
@ -909,15 +794,15 @@ proc typeNameToModuleName(name: string): string =
proc newRegisterClassNode(typ: GodotType): PNode =
newCall("registerClass",
ident(typ.name),
newStrLit(typ.godotName),
newCStringLit(typ.godotName),
ident("true") # isNative
)
proc genSingletonDecl(typ: string): PNode =
result = newPNode(nkVarSection)
result = newNode(nkVarSection)
let name = singletonName(typ)
let threadvar = newPNode(nkPragma).addChain(ident("threadvar"))
result.add(newIdentDefs(newPNode(nkPragmaExpr).addChain(name, threadvar),
let threadvar = newNode(nkPragma).addChain(ident("threadvar"))
result.add(newIdentDefs(newNode(nkPragmaExpr).addChain(name, threadvar),
ident(typ)))
proc shouldExport(typ: GodotType, types: Table[string, GodotType]): bool =
@ -934,9 +819,9 @@ proc sortByDerivedCount(types: Table[string, GodotType]): seq[GodotType] =
proc genTypeFile(types: Table[string, GodotType], targetDir: string) =
let sortedTypes = sortByDerivedCount(types)
let godotApiTypesTree = newPNode(nkStmtList)
godotApiTypesTree.add(newPNode(nkImportStmt).addChain(ident("godot")))
let typeSection = newPNode(nkTypeSection)
let godotApiTypesTree = newNode(nkStmtList)
godotApiTypesTree.add(newNode(nkImportStmt).addChain(ident("godot")))
let typeSection = newNode(nkTypeSection)
godotApiTypesTree.add(typeSection)
for typ in sortedTypes:
@ -947,7 +832,7 @@ proc genTypeFile(types: Table[string, GodotType], targetDir: string) =
if not typ.shouldExport(types): continue
let regNode = newCall("registerClass",
ident(typ.name),
newStrLit(typ.godotName),
newCStringLit(typ.godotName),
ident("true") # isNative
)
godotApiTypesTree.add(regNode)
@ -958,10 +843,10 @@ proc genSingletonWithDerived(tree: PNode, typ: GodotType,
types: Table[string, GodotType]) =
let sortedTypes = sortByDerivedCount(types)
var parents = initHashSet[string]()
var parents = initSet[string]()
parents.incl(typ.name)
let typeDecl = newPNode(nkTypeSection)
let typeDecl = newNode(nkTypeSection)
tree.add(typeDecl)
proc genDecl(typ: GodotType) =
newRefTypeNode(typeDecl, typ.name, typ.baseName, typ.doc,
@ -990,7 +875,7 @@ proc genApi*(targetDir: string, apiJsonFile: string) =
let className = godotClassName.replace("_", "")
var baseClassName = obj["base_class"].str
baseClassName = toNimType(baseClassName.replace("_", ""))
if baseClassName.len == 0:
if baseClassName.isNil or baseClassName.len == 0:
baseClassName = "NimGodotObject"
var doc = ""
for attr in classAttributes:
@ -1011,17 +896,17 @@ proc genApi*(targetDir: string, apiJsonFile: string) =
for typ in types.values():
let moduleName = typeNameToModuleName(typ.name)
echo "Generating ", moduleName, ".nim..."
let tree = newPNode(nkStmtList)
let importStmt = newPNode(nkImportStmt)
let tree = newNode(nkStmtList)
let importStmt = newNode(nkImportStmt)
importStmt.add(ident("godot"))
importStmt.add(ident("godottypes"))
importStmt.add(ident("godotinternal"))
tree.add(importStmt)
let exportStmt = newPNode(nkExportStmt)
let exportStmt = newNode(nkExportStmt)
exportStmt.add(ident("godottypes"))
tree.add(exportStmt)
var methodBindRegsitry = initHashSet[string]()
var methodBindRegsitry = initSet[string]()
let obj = typ.jsonNode
if typ.baseName != "NimGodotObject":
let baseModule = typeNameToModuleName(typ.baseName)
@ -1082,6 +967,7 @@ when isMainModule:
import os
const usage = """Tool for wrapping Godot API into Nim
godotapigen [godot binary path] [target directory]"""
proc main() =

View file

@ -1,8 +1,8 @@
# Copyright 2018 Xored Software, Inc.
import internal/godotinternaltypes, internal/godotarrays,
internal/godotnodepaths, internal/godotpoolarrays, internal/godotstrings,
internal/godotvariants, internal/godotdictionaries
import internal.godotinternaltypes, internal.godotarrays,
internal.godotnodepaths, internal.godotpoolarrays, internal.godotstrings,
internal.godotvariants, internal.godotdictionaries
import gdnativeapi
export godotinternaltypes, godotarrays, godotnodepaths, godotpoolarrays,
@ -93,11 +93,6 @@ proc getClassName*(o: ptr GodotObject): string =
# There are physics type not known by ClassDB
result = result[0..result.len-3]
proc getClassNameRaw*(o: ptr GodotObject): GodotString =
if getClassMethodBind.isNil:
getClassMethodBind = getMethod(cstring"Object", cstring"get_class")
getClassMethodBind.ptrCall(o, nil, addr result)
proc getGodotSingleton*(name: cstring): ptr GodotObject {.inline.} =
getGDNativeAPI().globalGetSingleton(name)

View file

@ -1,19 +0,0 @@
# Copyright 2019 Xored Software, Inc.
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
import macros, sets
proc strVal(n: NimNode): string {.used.} =
case n.kind
of nnkIdent:
$n.ident
of nnkSym:
$n.symbol
else:
macros.strVal(n)
proc toHashSet[A](keys: openArray[A]): HashSet[A] {.inline, used.} =
toSet(keys)
proc initHashSet[A](): HashSet[A] {.inline, used.} =
initSet[A]()

View file

@ -1,6 +1,6 @@
# Copyright (c) 2018 Xored Software, Inc.
import godotinternaltypes, gdnativeapi, core/godotcoretypes
import godotinternaltypes, gdnativeapi, core.godotcoretypes
proc len*(self: GodotArray): cint {.inline.} =
getGDNativeAPI().arraySize(self)

View file

@ -66,8 +66,6 @@ type
GodotString* {.byref.} = object
data: array[sizeof(int), byte]
cwchar_t* {.importc: "wchar_t", nodecl.} = object
GodotCharString* {.byref.} = object
data: array[sizeof(int), byte]
@ -116,8 +114,7 @@ type
expected*: VariantType
GodotVariant* {.byref.} = object
data: array[2, int64]
data2: int
data: array[4 + sizeof(int) div 4, float32]
type GodotMethodBind* = object
@ -308,6 +305,3 @@ type
GodotClassConstructor* = proc (): ptr GodotObject {.
noconv, gcsafe, locks: 0, raises: [], tags: [].}
template offset*[T](p: ptr T, offset: int): ptr T =
cast[ptr T](cast[ByteAddress](p) +% (offset * sizeof(T)))

View file

@ -1,7 +1,10 @@
# Copyright (c) 2018 Xored Software, Inc.
import godotinternaltypes, gdnativeapi
import core/godotcoretypes
import core.godotcoretypes
proc offset[T](p: ptr T, offset: int): ptr T {.inline.} =
cast[ptr T](cast[ByteAddress](p) +% (offset * sizeof(T)))
template genPoolArrayAPI(ArrayT, initIdent, DataT,
newProc, newCopyProc, newWithArrayProc, appendProc,
@ -45,15 +48,7 @@ template genPoolArrayAPI(ArrayT, initIdent, DataT,
getGDNativeAPI().setProc(self, idx, data)
proc `[]`*(self: ArrayT; idx: cint): DataT {.inline.} =
let data = getGDNativeAPI().getProc(self, idx)
when DataT is Color:
data.toColor()
elif DataT is Vector2:
data.toVector2()
elif DataT is Vector3:
data.toVector3()
else:
data
getGDNativeAPI().getProc(self, idx)
proc len*(self: ArrayT): cint {.inline.} =
getGDNativeAPI().sizeProc(self)

View file

@ -22,12 +22,6 @@ proc `&`*(self, b: GodotString): GodotString {.inline.} =
proc deinit*(self: var GodotString) {.inline.} =
getGDNativeAPI().stringDestroy(self)
proc len*(self: GodotString): cint {.inline.} =
getGDNativeAPI().stringLength(self)
proc dataPtr*(self: GodotString): ptr cwchar_t {.inline.} =
getGDNativeAPI().stringWideStr(self)
proc `$`*(self: GodotString): string =
## Converts the ``GodotString`` into Nim string
var charStr = getGDNativeAPI().stringUtf8(self)
@ -35,16 +29,14 @@ proc `$`*(self: GodotString): string =
result = newString(length)
copyMem(addr result[0], getGDNativeAPI().charStringGetData(charStr), length)
getGDNativeAPI().charStringDestroy(charStr)
assert(result[length] == '\0')
proc toGodotString*(s: string): GodotString {.inline.} =
## Converts the Nim string into ``GodotString``
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
if s.isNil:
initGodotString(result)
else:
initGodotString(result, cstring(s), cint(s.len + 1))
else:
initGodotString(result, cstring(s), cint(s.len + 1))
initGodotString(result, unsafeAddr s[0], cint(s.len + 1))
proc toGodotString*(s: cstring): GodotString {.inline.} =
## Converts the cstring into ``GodotString``

View file

@ -1,6 +1,6 @@
# Copyright 2018 Xored Software, Inc.
import core/godotcoretypes, godotinternaltypes, gdnativeapi
import core.godotcoretypes, godotinternaltypes, gdnativeapi
proc getType*(p: GodotVariant): VariantType {.inline.} =
getGDNativeAPI().variantGetType(p)
@ -121,34 +121,34 @@ proc asGodotString*(self: GodotVariant): GodotString {.inline.} =
getGDNativeAPI().variantAsString(self)
proc asVector2*(self: GodotVariant): Vector2 {.inline.} =
getGDNativeAPI().variantAsVector2(self).toVector2()
getGDNativeAPI().variantAsVector2(self)
proc asRect2*(self: GodotVariant): Rect2 {.inline.} =
getGDNativeAPI().variantAsRect2(self).toRect2()
getGDNativeAPI().variantAsRect2(self)
proc asVector3*(self: GodotVariant): Vector3 {.inline.} =
getGDNativeAPI().variantAsVector3(self).toVector3()
getGDNativeAPI().variantAsVector3(self)
proc asTransform2D*(self: GodotVariant): Transform2D {.inline.} =
getGDNativeAPI().variantAsTransform2D(self).toTransform2D()
getGDNativeAPI().variantAsTransform2D(self)
proc asPlane*(self: GodotVariant): Plane {.inline.} =
getGDNativeAPI().variantAsPlane(self).toPlane()
getGDNativeAPI().variantAsPlane(self)
proc asQuat*(self: GodotVariant): Quat {.inline.} =
getGDNativeAPI().variantAsQuat(self).toQuat()
getGDNativeAPI().variantAsQuat(self)
proc asAABB*(self: GodotVariant): AABB {.inline.} =
getGDNativeAPI().variantAsAABB(self).toAABB()
getGDNativeAPI().variantAsAABB(self)
proc asBasis*(self: GodotVariant): Basis {.inline.} =
getGDNativeAPI().variantAsBasis(self).toBasis()
getGDNativeAPI().variantAsBasis(self)
proc asTransform*(self: GodotVariant): Transform {.inline.} =
getGDNativeAPI().variantAsTransform(self).toTransform()
getGDNativeAPI().variantAsTransform(self)
proc asColor*(self: GodotVariant): Color {.inline.} =
getGDNativeAPI().variantAsColor(self).toColor()
getGDNativeAPI().variantAsColor(self)
proc asNodePath*(self: GodotVariant): GodotNodePath {.inline.} =
getGDNativeAPI().variantAsNodePath(self)

View file

@ -1,8 +1,8 @@
# Copyright 2018 Xored Software, Inc.
import macros, tables, typetraits, strutils, sets, sequtils, options, algorithm
import godotinternal, internal/godotvariants
import godotnim, core/variants
import macros, tables, typetraits
import godotinternal, internal.godotvariants
import godotnim, core.variants
type
VarDecl = ref object
@ -10,8 +10,8 @@ type
typ: NimNode
defaultValue: NimNode
isNoGodot: bool
hint: Option[string]
hintStr: Option[string]
hint: string
hintStr: string
usage: NimNode
isExported: bool
@ -32,8 +32,6 @@ type
ParseError = object of Exception
include "internal/backwardcompat.inc.nim"
proc godotToNim[T](val: Variant): (T, ConversionResult) =
mixin fromVariant
result[1] = fromVariant(result[0], val)
@ -53,15 +51,15 @@ template parseError(node: NimNode, msg: string) =
proc extractNames(definition: NimNode):
tuple[name, parentName: string] =
if definition.kind == nnkIdent:
result.name = definition.strVal
result.name = $(definition.ident)
else:
if not (definition.kind == nnkInfix and
definition[0].strVal == "of"):
definition[0].ident == toNimIdent("of")):
parseError(definition, "invalid type definition")
result.name = definition[1].strVal
result.name = $(definition[1].ident)
case definition[2].kind:
of nnkIdent:
result.parentName = definition[2].strVal
result.parentName = $definition[2].ident
else:
parseError(definition[2], "parent type expected")
@ -74,13 +72,13 @@ proc newCStringLit(s: string): NimNode {.compileTime.} =
newNimNode(nnkCallStrLit).add(ident("cstring"), newRStrLit(s))
iterator pragmas(node: NimNode):
tuple[key: string, value: NimNode, index: int] =
tuple[key: NimIdent, value: NimNode, index: int] =
assert node.kind in {nnkPragma, nnkEmpty}
for index in countdown(node.len - 1, 0):
if node[index].kind == nnkExprColonExpr:
yield (node[index][0].strVal, node[index][1], index)
yield (node[index][0].ident, node[index][1], index)
elif node[index].kind == nnkIdent:
yield (node[index].strVal, nil, index)
yield (node[index].ident, nil, index)
proc removePragmaNode(statement: NimNode,
pname: string): NimNode {.compileTime.} =
@ -93,8 +91,9 @@ proc removePragmaNode(statement: NimNode,
result = nil
var pragmas = if RoutineNodes.contains(statement.kind): statement.pragma()
else: statement[1]
let pnameIdent = toNimIdent(pname)
for ident, val, i in pragmas(pragmas):
if ident.eqIdent(pname):
if ident == pnameIdent:
pragmas.del(i)
return val
@ -105,18 +104,19 @@ proc removePragma(statement: NimNode, pname: string): bool =
return false
var pragmas = if RoutineNodes.contains(statement.kind): statement.pragma()
else: statement[1]
let pnameIdent = toNimIdent(pname)
for ident, val, i in pragmas(pragmas):
if ident.eqIdent(pname):
if ident == pnameIdent:
pragmas.del(i)
return true
proc removeStrPragma(statement: NimNode,
pname: string): Option[string] {.compileTime.} =
pname: string): string {.compileTime.} =
## Removes the pragma from the node and returns value of the pragma
## Works for routine nodes or nnkPragmaExpr
let node = removePragmaNode(statement, pname)
result = if node.isNil: none(string)
else: some($node)
result = if node.isNil: nil
else: $node
proc isExported(node: NimNode): bool {.compileTime.} =
if node.kind == nnkPragmaExpr:
@ -172,15 +172,10 @@ proc parseMethod(meth: NimNode): MethodDecl =
proc parseVarSection(decl: NimNode): seq[VarDecl] =
assert(decl.kind == nnkVarSection)
for i in 0..<decl.len:
if i == 0:
result = identDefsToVarDecls(decl[i])
else:
result.add(identDefsToVarDecls(decl[i]))
result = identDefsToVarDecls(decl[0])
proc parseType(ast: NimNode): ObjectDecl =
let definition = ast[0]
let body = ast[^1]
proc parseType(definition, callSite: NimNode): ObjectDecl =
let body = callSite[^1]
result = ObjectDecl(
fields: newSeq[VarDecl](),
methods: newSeq[MethodDecl]()
@ -188,17 +183,17 @@ proc parseType(ast: NimNode): ObjectDecl =
(result.name, result.parentName) = extractNames(definition)
var isTool = false
for i in 1..(ast.len - 2):
let option = ast[i]
for i in 2..(callSite.len - 2):
let option = callSite[i]
if option.kind != nnkIdent:
parseError(option, "type specifier expected")
if option.strVal == "tool":
if option.ident == toNimIdent("tool"):
isTool = true
else:
parseError(option, "valid type specifier expected")
result.isTool = isTool
if result.parentName.len == 0: result.parentName = "Object"
if result.parentName.isNil: result.parentName = "Object"
for statement in body:
case statement.kind:
of nnkVarSection:
@ -215,7 +210,7 @@ proc parseType(ast: NimNode): ObjectDecl =
macro invokeVarArgs(procIdent, objIdent;
minArgs, maxArgs: static[int], numArgsIdent,
argSeqIdent; argTypes: seq[NimNode],
hasReturnValue, isStaticCall: static[bool]): untyped =
hasReturnValue, isStaticCall: static[bool]): typed =
## Produces statement in form:
##
## .. code-block:: nim
@ -272,12 +267,8 @@ macro invokeVarArgs(procIdent, objIdent;
invocation = newNimNode(nnkCall).add(ident("procCall"), invocation)
if hasReturnValue:
let resultVariant = genSym(nskLet, "ret")
branchBody.add(
newNimNode(nnkLetSection).add(
newIdentDefs(resultVariant, ident("Variant"), newCall("toVariant", invocation))))
let theCall = newNimNode(nnkBracketExpr).add(newNimNode(nnkDotExpr).add(
resultVariant, ident("godotVariant")))
newCall("toVariant", invocation), ident("godotVariant")))
branchBody.add(getAst(initGodotVariantCall(ident("result"), theCall)))
else:
branchBody.add(invocation)
@ -292,7 +283,7 @@ macro invokeVarArgs(procIdent, objIdent;
" arguments, but got " & $numArgs)
result.add(newNimNode(nnkElse).add(getAst(
printInvokeErr(procIdent.strVal, minArgs, maxArgs, numArgsIdent))))
printInvokeErr($procIdent.ident, minArgs, maxArgs, numArgsIdent))))
proc typeError(nimType: string, value: string, godotType: VariantType,
className: cstring, propertyName: cstring): string =
@ -400,9 +391,10 @@ template registerGodotField(classNameLit, classNameIdent, propNameLit,
getFunc: getFuncIdent
)
mixin godotTypeInfo
const typeInfo = when compiles(godotTypeInfo(propTypeIdent)):
godotTypeInfo(propTypeIdent)
else: GodotTypeInfo()
static:
var typeInfo: GodotTypeInfo
when compiles(godotTypeInfo(propTypeIdent)):
typeInfo = godotTypeInfo(propTypeIdent)
const hintStr = when hintStrLit != "NIM":
hintStrLit
else:
@ -428,6 +420,9 @@ template registerGodotField(classNameLit, classNameIdent, propNameLit,
unsafeAddr attr, setFunc, getFunc)
hintStrGodot.deinit()
static:
import strutils, sets, sequtils
proc toGodotStyle(s: string): string {.compileTime.} =
result = newStringOfCap(s.len + 10)
for c in s:
@ -448,7 +443,7 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
let objTy = newNimNode(nnkObjectTy)
typeDef.add(newNimNode(nnkRefTy).add(objTy))
objTy.add(newEmptyNode())
if obj.parentName.len == 0:
if obj.parentName.isNil:
objTy.add(newEmptyNode())
else:
objTy.add(newNimNode(nnkOfInherit).add(ident(obj.parentName)))
@ -458,8 +453,7 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
let initBody = newStmtList()
for decl in obj.fields:
if not decl.defaultValue.isNil and decl.defaultValue.kind != nnkEmpty:
initBody.add(newNimNode(nnkAsgn).add(newDotExpr(ident("self"), decl.name),
decl.defaultValue))
initBody.add(newNimNode(nnkAsgn).add(decl.name, decl.defaultValue))
let name = if not decl.isExported: decl.name
else: postfix(decl.name, "*")
recList.add(newIdentDefs(name, decl.typ))
@ -485,7 +479,6 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
else:
initMethod.body.insert(0, initBody)
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
# {.this: self.} for convenience
result.add(newNimNode(nnkPragma).add(newNimNode(nnkExprColonExpr).add(
ident("this"), ident("self")
@ -509,25 +502,23 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
result.add(meth.nimNode)
# Register Godot object
let parentName = if obj.parentName.len == 0: newStrLitNode("Object")
let parentName = if obj.parentName.isNil: newStrLitNode("Object")
else: newStrLitNode(obj.parentName)
let classNameLit = newStrLitNode(obj.name)
let classNameLit = newCStringLit(obj.name)
let classNameIdent = ident(obj.name)
let isRef: bool = if obj.parentName.len == 0: false
let isRef: bool = if obj.parentName.isNil: false
else: obj.parentName in refClasses
# Wrapping bools with a newLit is required as a temporary workaround for
# https://github.com/nim-lang/Nim/issues/7375
result.add(getAst(
registerGodotClass(classNameIdent, classNameLit, newLit(isRef), parentName,
genSym(nskProc, "createFunc"), newLit(obj.isTool))))
registerGodotClass(classNameIdent, classNameLit, isRef, parentName,
genSym(nskProc, "createFunc"), obj.isTool)))
# Register fields (properties)
for field in obj.fields:
if field.isNoGodot: continue
let hintStr = if field.hintStr.isNone: "NIM"
else: field.hintStr.get
let hint = if field.hint.isNone: "NIM"
else: field.hint.get
let hintStr = if field.hintStr.isNil: "NIM"
else: field.hintStr
let hint = if field.hint.isNil: "NIM"
else: field.hint
let usage =
if field.usage.isNil:
newLit(ord(GodotPropertyUsage.Default) or
@ -550,23 +541,17 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
template registerGodotMethod(classNameLit, classNameIdent, methodNameIdent,
methodNameLit, minArgs, maxArgs,
argTypes, methFuncIdent, hasReturnValue) =
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
{.emit: """/*TYPESECTION*/
N_NOINLINE(void, setStackBottom)(void* thestackbottom);
""".}
proc methFuncIdent(obj: ptr GodotObject, methodData: pointer,
userData: pointer, numArgs: cint,
args: var array[MAX_ARG_COUNT, ptr GodotVariant]):
GodotVariant {.noconv.} =
var stackBottom {.volatile.}: pointer
stackBottom = addr(stackBottom)
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
{.emit: """
setStackBottom((void*)(&`stackBottom`));
""".}
else:
nimGC_setStackBottom(stackBottom)
let self = cast[classNameIdent](userData)
const isStaticCall = methodNameLit == cstring"_ready" or
methodNameLit == cstring"_process" or
@ -610,7 +595,7 @@ N_NOINLINE(void, setStackBottom)(void* thestackbottom);
let hasReturnValueBool = not (meth.returnType.isNil or
meth.returnType.kind == nnkEmpty or
(meth.returnType.kind == nnkIdent and
meth.returnType.strVal == "void"))
meth.returnType.ident == toNimIdent("void")))
let hasReturnValue = if hasReturnValueBool: ident("true")
else: ident("false")
result.add(getAst(
@ -636,7 +621,11 @@ N_NOINLINE(void, setStackBottom)(void* thestackbottom);
GodotMethodAttributes(), refDec)
result.add(getAst(registerRefIncDec(classNameLit)))
macro gdobj*(ast: varargs[untyped]): untyped =
{.push warning[Deprecated]: off.}
# immediate macros are deprecated, but `untyped` doesn't make it immediate,
# as the warning and the documentation claim.
macro gdobj*(definition: untyped, body: typed): typed {.immediate.} =
## Generates Godot type. Self-documenting example:
##
## .. code-block:: nim
@ -644,7 +633,7 @@ macro gdobj*(ast: varargs[untyped]): untyped =
##
## gdobj MyObj of Node:
## var myField: int
## ## Not exported to Godot (i.e. editor and scripts will not see this field).
## ## Not exported to Godot (i.e. editor will not see this field).
##
## var myString* {.gdExport, hint: Length, hintStr: "20".}: string
## ## Exported to Godot as ``my_string``.
@ -657,12 +646,12 @@ macro gdobj*(ast: varargs[untyped]): untyped =
## ## Exported methods are exported to Godot by default,
## ## and their Godot names are prefixed with ``_``
## ## (in this case ``_ready``)
## print("I am ready! myString is: " & self.myString)
## print("I am ready! myString is: " & myString)
##
## proc myProc*() {.gdExport.} =
## ## Exported to Godot as ``my_proc``
## print("myProc is called! Incrementing myField.")
## inc self.myField
## inc myField
##
## If parent type is omitted, the type is inherited from ``Object``.
##
@ -680,5 +669,7 @@ macro gdobj*(ast: varargs[untyped]): untyped =
## `gdnew <godotnim.html#gdnew>`_ or by using
## `load <godotapi/resource_loader.html#load,string,string,bool>`_ or any other way
## that you can find in `Godot API <index.html#modules-godot-api>`_.
let typeDef = parseType(ast)
let typeDef = parseType(definition, callsite())
result = genType(typeDef)
{.push warning[Deprecated]: on.}

View file

@ -1,14 +1,14 @@
# Copyright 2018 Xored Software, Inc.
import tables, typetraits, macros, unicode, strutils, sets, options
import tables, typetraits, macros
import gdnativeapi
import core/godotcoretypes, core/godotbase
import core/vector2, core/rect2,
core/vector3, core/transform2d,
core/planes, core/quats, core/aabb,
core/basis, core/transforms, core/colors,
core/nodepaths, core/rids, core/dictionaries,
core/arrays, core/poolarrays, core/variants
import core.godotcoretypes, core.godotbase
import core.vector2, core.rect2,
core.vector3, core.transform2d,
core.planes, core.quats, core.aabb,
core.basis, core.transforms, core.colors,
core.nodepaths, core.rids, core.dictionaries,
core.arrays, core.poolarrays, core.variants
import godotinternal
## This module defines ``NimGodotObject`` and ``toVariant``/``fromVariant``
@ -31,7 +31,7 @@ type
## The base type all Godot types inherit from.
## Manages lifecycle of the wrapped ``GodotObject``.
godotObject: ptr GodotObject
linkedObjectPtr: pointer
linkedObject: NimGodotObject
## Wrapper around native object that is the container of the Nim "script"
## This is needed for `of` checks and `as` conversions to work as
## expected. For example, Nim type may inherit from ``Spatial``, but the
@ -39,8 +39,6 @@ type
## ``Particles`` is valid, but Nim type system is not aware of that.
## This works in both directions - for linked native object this
## reference points to Nim object.
## This is stored as a raw pointer to avoid reference cycles and therefore
## improve GC performance.
isRef*: bool
isFinalized: bool
isNative: bool
@ -91,71 +89,21 @@ type
## values.
## See documentation of ``GodotPropertyHint`` for description of formats.
FNV1Hash = uint32
include "internal/backwardcompat.inc.nim"
proc isFinalized*(obj: NimGodotObject): bool {.inline.} =
obj.isFinalized
var classRegistry {.threadvar.}: TableRef[FNV1Hash, ObjectInfo]
var classRegistryStatic* {.compileTime.}: TableRef[FNV1Hash, ObjectInfo]
var classRegistry {.threadvar.}: TableRef[cstring, ObjectInfo]
var classRegistryStatic* {.compileTime.}: TableRef[cstring, ObjectInfo]
## Compile-time variable used for implementation of several procedures
## and macros
static:
classRegistryStatic = newTable[FNV1Hash, ObjectInfo]()
classRegistryStatic = newTable[cstring, ObjectInfo]()
static:
import sets, strutils
var nativeClasses {.compileTime.} = newSeq[string]()
var refClasses* {.compileTime.} = newSeq[string]()
template initFNV1Hash(hash: var FNV1Hash) =
hash = 0x811c9dc5'u32
template appendFNV1Hash(hash: var FNV1Hash, val: uint8) =
block:
let u64hash = hash.uint64
hash = ((
u64hash +
(u64hash shl 1'u64) +
(u64hash shl 4'u64) +
(u64hash shl 7'u64) +
(u64hash shl 8'u64) +
(u64hash shl 24'u64)) and 0xFFFFFFFF'u32).uint32 xor val
{.push stackTrace:off.}
proc lsb(c: ptr cwchar_t): char {.noinit, inline.} =
{.emit: [result, " = (char)(", c[]," & 0xFF);"]}
{.pop.}
proc fnv1Hash(godotClassName: GodotString): FNV1Hash =
var charsCount = godotClassName.len
let charsPtr = godotClassName.dataPtr
if charsCount > 2 and
charsPtr.offset(charsCount - 2).lsb == 'S' and
charsPtr.offset(charsCount - 1).lsb == 'W':
charsCount -= 2
initFNV1Hash(result)
for i in 0..<charsCount:
let c = charsPtr.offset(i).lsb
appendFNV1Hash(result, c.uint8)
proc fnv1Hash(godotClassName: string): FNV1Hash {.compileTime.} =
var charsCount = godotClassName.len
if godotClassName.endsWith("SW"):
charsCount -= 2
initFNV1Hash(result)
var i = 0
for rune in runes(godotClassName):
if i >= charsCount:
break
let firstByte = uint8(rune.uint64 and 0xFF'u64)
appendFNV1Hash(result, firstByte)
# We don't really need to calculate hash of other bytes,
# since we only use hashes of ASCII strings
inc i
proc getClassName*(o: NimGodotObject): string =
o.godotObject.getClassName()
@ -187,15 +135,12 @@ proc deinit*(obj: NimGodotObject) =
obj.godotObject.deinit()
obj.godotObject = nil
proc linkedObject(obj: NimGodotObject): NimGodotObject {.inline.} =
cast[NimGodotObject](obj.linkedObjectPtr)
proc nimGodotObjectFinalizer*[T: NimGodotObject](obj: T) =
if obj.godotObject.isNil or obj.isNative: return
# important to set it before so that ``unreference`` is aware
obj.isFinalized = true
if (obj.isRef or not obj.linkedObject.isNil and obj.linkedObject.isRef) and
obj.godotObject.unreference():
if obj.isRef and obj.godotObject.unreference() or
not obj.linkedObject.isNil:
obj.deinit()
macro baseNativeType(T: typedesc): cstring =
@ -209,7 +154,7 @@ macro baseNativeType(T: typedesc): cstring =
if typeName == "NimGodotObject":
break
t = getType(t[1][1])
if baseT.len == 0:
if baseT.isNil:
result = newNilLit()
else:
let rStr = newNimNode(nnkRStrLit)
@ -234,12 +179,12 @@ macro isResource(T: typedesc): bool =
result = if inherits(getType(T), "Resource"): ident("true")
else: ident("false")
template registerClass*(T: typedesc; godotClassName: string or cstring,
template registerClass*(T: typedesc; godotClassName: cstring,
native: bool) =
## Registers the specified Godot type.
## Used by ``gdobj`` macro and `godotapigen <godotapigen.html>`_.
if classRegistry.isNil:
classRegistry = newTable[FNV1Hash, ObjectInfo]()
classRegistry = newTable[cstring, ObjectInfo]()
let constructor = proc(): NimGodotObject =
var t: T
new(t, nimGodotObjectFinalizer[T])
@ -253,22 +198,14 @@ template registerClass*(T: typedesc; godotClassName: string or cstring,
isNative: native,
isRef: isRef
)
classRegistry[fnv1Hash($godotClassName)] = objInfo
classRegistry[godotClassName] = objInfo
static:
let objInfoStatic = ObjectInfo(
baseNativeClass: base,
isNative: native,
isRef: isRef,
)
let nameHash = fnv1Hash($godotClassName)
if not classRegistryStatic.contains(nameHash):
classRegistryStatic[nameHash] = objInfoStatic
elif not endsWith($godotClassName, "SW"):
# For simplicity we assume that all class names must have
# different hashes
# If this exception is ever raised, I guess, we should
# implement a proper collision resolving
raise newException(Exception, "Hash collision " & $godotClassName)
classRegistryStatic[godotClassName] = objInfoStatic
when isRef:
static:
refClasses.add(T.name)
@ -277,10 +214,10 @@ template registerClass*(T: typedesc; godotClassName: string or cstring,
nativeClasses.add(T.name)
proc newNimGodotObject[T: NimGodotObject](
godotObject: ptr GodotObject, godotClassName: GodotString, noRef: bool): T =
godotObject: ptr GodotObject, godotClassName: cstring, noRef: bool): T =
assert(not classRegistry.isNil)
assert(not godotObject.isNil)
let objInfo = classRegistry.getOrDefault(fnv1Hash(godotClassName))
let objInfo = classRegistry.getOrDefault(godotClassName)
if objInfo.constructor.isNil:
printError("Nim constructor not found for class " & $godotClassName)
else:
@ -303,11 +240,9 @@ proc asNimGodotObject*[T: NimGodotObject](
# Could be data from other bindings
result = nil
if result.isNil:
var classNameStr = godotObject.getClassNameRaw()
result = newNimGodotObject[T](
godotObject, classNameStr,
godotObject, cstring(godotObject.getClassName()),
forceNativeObject or noRef)
deinit(classNameStr)
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
newVariant(obj.godotObject)
@ -332,35 +267,45 @@ proc `as`*[T: NimGodotObject](obj: NimGodotObject, t: typedesc[T]): T =
## This can be used either in dot notation (``node.as(Button)``) or
## infix notation (``node as Button``).
if obj.isNil: return nil
if system.`of`(obj, T):
if obj of T:
result = T(obj)
elif not obj.linkedObject.isNil and system.`of`(obj.linkedObject, T):
elif not obj.linkedObject.isNil and obj.linkedObject of T:
result = T(obj.linkedObject)
else:
when not defined(release):
printError("Failed to cast object to " &
T.name & "\n" & getStackTrace())
result = nil
proc `of`*[T1, T2: NimGodotObject](obj: T1, t: typedesc[T2]): bool {.inline.} =
proc `of`*[T: NimGodotObject](obj: NimGodotObject,
t: typedesc[T]): bool {.inline.} =
## Godot-specific inheritance check.
not obj.isNil and (system.`of`(obj, T2) or system.`of`(obj.linkedObject, T2))
not obj.isNil and (system.`of`(obj, T) or system.`of`(obj.linkedObject, T))
proc newRStrLit(s: string): NimNode {.compileTime.} =
result = newNimNode(nnkRStrLit)
result.strVal = s
macro toGodotName(T: typedesc): untyped =
static:
import strutils
macro toGodotName(T: typedesc): string =
var godotName: string
if T is GodotString or T is string:
newStrLitNode"String"
godotName = "String"
elif T is SomeFloat:
newStrLitNode"float"
godotName = "float"
elif T is SomeUnsignedInt or T is SomeSignedInt:
newStrLitNode"int"
else:
let nameStr = ((T.getType()[1][1].strVal).split(':')[0])
case nameStr:
godotName = "int"
if godotName.isNil:
let nameStr: string = (($T.getType()[1][1].symbol).split(':')[0])
godotName = case nameStr:
of "File", "Directory", "Thread", "Mutex", "Semaphore":
newStrLitNode("_" & nameStr)
"_" & nameStr
else:
newStrLitNode(nameStr)
nameStr
result = newLit(godotName)
macro asCString(s: static[string]): cstring =
result = newNimNode(nnkCallStrLit).add(
@ -419,18 +364,17 @@ proc newOwnObj[T: NimGodotObject](name: cstring): T =
proc gdnew*[T: NimGodotObject](): T =
## Instantiates new object of type ``T``.
const godotName = toGodotName(T)
const cGodotName = asCString(godotName)
const objInfo = classRegistryStatic[fnv1Hash(godotName)]
const godotName = asCString(toGodotName(T))
const objInfo = classRegistryStatic[godotName]
when objInfo.isNative:
let godotObject = getClassConstructor(cGodotName)()
let godotObject = getClassConstructor(godotName)()
new(result, nimGodotObjectFinalizer[T])
result.godotObject = godotObject
when objInfo.isRef:
godotObject.initRef()
result.isRef = true
else:
result = newOwnObj[T](cGodotName)
result = newOwnObj[T](godotName)
proc newCallError*(err: VariantCallError): ref CallError =
## Instantiates ``CallError`` from Godot ``err``.
@ -470,17 +414,14 @@ proc setGodotObject*(nimObj: NimGodotObject, obj: ptr GodotObject) {.inline.} =
proc setNativeObject*(nimObj: NimGodotObject,
nativeObj: NimGodotObject) {.inline.} =
## Used from Godot constructor produced by ``gdobj`` macro. Do not call.
GC_ref(nativeObj)
nimObj.linkedObjectPtr = cast[pointer](nativeObj)
nativeObj.linkedObjectPtr = cast[pointer](nimObj)
nimObj.linkedObject = nativeObj
nativeObj.linkedObject = nimObj
nativeObj.isNative = true
proc removeGodotObject*(nimObj: NimGodotObject) {.inline.} =
## Used from Godot destructor produced by ``gdobj`` macro. Do not call.
GC_unref(nimObj.linkedObject)
nimObj.godotObject = nil
nimObj.linkedObject.godotObject = nil
nimObj.linkedObjectPtr = nil
proc `==`*(self, other: NimGodotObject): bool {.inline.} =
## Compares objects by referential equality.
@ -612,11 +553,7 @@ proc fromVariant*[T: SomeSignedInt or SomeUnsignedInt](
else: uint64)
intVal = when T is SomeSignedInt: val.asInt()
else: val.asUint()
const highT = when T is SomeSignedInt: high(T).int64
else: high(T).uint64
const lowT = when T is SomeSignedInt: low(T).int64
else: low(T).uint64
if intVal > highT or intVal < lowT:
if intVal > high(T) or intVal < low(T):
result = ConversionResult.RangeError
else:
self = T(intVal)
@ -720,10 +657,7 @@ proc fromVariant*(s: var string, val: Variant): ConversionResult =
if val.getType() == VariantType.String:
s = val.asString()
elif val.getType() == VariantType.Nil:
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
s = nil
else:
s = ""
else:
result = ConversionResult.TypeError
@ -797,31 +731,10 @@ proc fromVariant*[T: array](s: var T, val: Variant): ConversionResult =
else:
result = ConversionResult.TypeError
proc godotTypeInfo*[T](OptT: typedesc[Option[T]]): GodotTypeInfo =
when compiles(godotTypeInfo(T)):
result.variantType = godotTypeInfo(T)
else:
result.variantType = VariantType.Nil
proc toVariant*[T](option: Option[T]): Variant =
if option.isSome():
result = toVariant(option.unsafeGet())
else:
result = newVariant()
proc fromVariant*[T](option: var Option[T], val: Variant): ConversionResult =
if val.getType() == VariantType.Nil:
option = none(T)
else:
var v: T
result = fromVariant(v, val)
if result == ConversionResult.OK:
option = some(result)
proc godotTypeInfo*(T: typedesc[Table|TableRef|OrderedTable|OrderedTableRef]): GodotTypeInfo {.inline.} =
proc godotTypeInfo*(T: typedesc[Table|TableRef]): GodotTypeInfo {.inline.} =
result.variantType = VariantType.Dictionary
proc toVariant*[T: Table or TableRef or OrderedTable or OrderedTableRef](t: T): Variant =
proc toVariant*[T: Table or TableRef](t: T): Variant =
when t is ref:
if t.isNil:
return newVariant()
@ -831,7 +744,7 @@ proc toVariant*[T: Table or TableRef or OrderedTable or OrderedTableRef](t: T):
dict[toVariant(k)] = toVariant(v)
result = newVariant(dict)
proc fromVariant*[T: Table or TableRef or OrderedTable or OrderedTableRef](t: var T,
proc fromVariant*[T: Table or TableRef](t: var T,
val: Variant): ConversionResult =
if val.getType() == VariantType.Nil:
when t is ref:
@ -841,12 +754,8 @@ proc fromVariant*[T: Table or TableRef or OrderedTable or OrderedTableRef](t: va
mixin fromVariant
when t is Table:
t = initTable[type(t.keys()), type(t.values())]()
elif t is TableRef:
t = newTable[type(t.keys()), type(t.values())]()
elif t is OrderedTable:
t = initOrderedTable[type(t.keys()), type(t.values())]()
else:
t = newOrderedTable[type(t.keys()), type(t.values())]()
t = newTable[type(t.keys()), type(t.values())]()
for k, v in dict:
var nimKey: type(t.keys())
var nimVal: type(t.values())
@ -864,9 +773,8 @@ proc fromVariant*[T: Table or TableRef or OrderedTable or OrderedTableRef](t: va
else:
result = ConversionResult.TypeError
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
{.emit: """/*TYPESECTION*/
N_LIB_EXPORT N_CDECL(void, NimMain)(void);
void NimMain(void);
N_NOINLINE(void, setStackBottom)(void* thestackbottom);
""".}
@ -881,16 +789,10 @@ proc godot_nativescript_init(handle: pointer) {.
nativeLibHandle = handle
var stackBottom {.volatile.}: pointer
stackBottom = addr(stackBottom)
{.emit: """
NimMain();
""".}
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
{.emit: """
setStackBottom((void*)(&`stackBottom`));
""".}
else:
nimGC_setStackBottom(stackBottom)
GC_fullCollect()
GC_disable()
@ -901,7 +803,6 @@ proc godot_gdnative_init(options: ptr GDNativeInitOptions) {.
proc godot_gdnative_terminate(options: ptr GDNativeTerminateOptions) {.
cdecl, exportc, dynlib.} =
if not options[].inEditor or not compileOption("threads"):
deallocHeap(runFinalizers = not options[].inEditor, allowGcAfterwards = false)
const nimGcStepLengthUs {.intdefine.} = 2000
@ -926,13 +827,9 @@ proc registerFrameCallback*(cb: proc () {.closure.}) =
proc godot_nativescript_frame() {.cdecl, exportc, dynlib.} =
var stackBottom {.volatile.}: pointer
stackBottom = addr(stackBottom)
when (NimMajor, NimMinor, NimPatch) < (0, 19, 0):
{.emit: """
setStackBottom((void*)(&`stackBottom`));
""".}
else:
nimGC_setStackBottom(stackBottom)
for cb in idleCallbacks:
cb()
GC_step(nimGcStepLengthUs, true, 0)

View file

@ -1,2 +1 @@
--path:"$projectdir/../"
-d:useRealtimeGc