changeset 77:4b16d2af316a

add: color functions + gamma correction
author Sam <sam@basx.dev>
date Tue, 07 Feb 2023 12:20:04 +0700
parents e5be7a25634e
children f67496a189cb
files examples/E10_pong.nim src/semicongine/color.nim src/semicongine/engine.nim src/semicongine/math/vector.nim
diffstat 4 files changed, 139 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/examples/E10_pong.nim	Mon Feb 06 23:36:55 2023 +0700
+++ b/examples/E10_pong.nim	Tue Feb 07 12:20:04 2023 +0700
@@ -9,18 +9,20 @@
     view: ViewProjectionTransform
 
 const
-  barcolor = Vec3([1'f, 0'f, 0'f])
+  barcolor = RGBfromHex("5A3F00").gamma(2.2)
   barSize = 0.1'f
   barWidth = 0.01'f
-  ballcolor = Vec3([0'f, 0'f, 0'f])
+  ballcolor = RGBfromHex("B17F08").gamma(2.2)
   levelRatio = 1
   ballSize = 0.01'f
+  backgroundColor = RGBAfromHex("FAC034").gamma(2.2)
+  ballSpeed = 60'f
 
 var
   uniforms = Uniforms()
   pipeline: RenderPipeline[Vertex, Uniforms]
   level: Thing
-  ballVelocity = Vec2([1'f, 1'f]).normalized * 30'f
+  ballVelocity = Vec2([1'f, 1'f]).normalized * ballSpeed
 
 proc globalUpdate(engine: var Engine; t, dt: float32) =
   var height = float32(engine.vulkan.frameSize.y) / float32(
@@ -44,9 +46,9 @@
 
   # loose
   if ball.transform.col(3).x - ballSize/2 <= 0:
-    ballVelocity = Vec2([1'f, 1'f]).normalized * 30'f
-    ball.transform[0, 3] = 0.5
-    ball.transform[1, 3] = 0.5
+    ballVelocity = Vec2([1'f, 1'f]).normalized * ballSpeed
+    ball.transform[0, 3] = width / 2
+    ball.transform[1, 3] = height / 2
 
   # bounce level
   if ball.transform.col(3).x + ballSize/2 > width: ballVelocity[
@@ -68,7 +70,7 @@
 
 when isMainModule:
   var myengine = igniteEngine("Pong")
-  myengine.fps = 60
+  myengine.maxFPS = 61
   level = newThing("Level")
   var playerbarmesh = quad[Vertex, Vec2, float32]()
   playerbarmesh.vertexData.color.data = @[barcolor, barcolor, barcolor, barcolor]
@@ -99,7 +101,7 @@
     vertexShader,
     fragmentShader
   )
-  pipeline.clearColor = Vec4([1.0'f, 1.0'f, 1.0'f, 1.0'f])
+  pipeline.clearColor = backgroundColor
   # show something
   myengine.run(pipeline, globalUpdate)
   pipeline.trash()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/color.nim	Tue Feb 07 12:20:04 2023 +0700
@@ -0,0 +1,42 @@
+import std/parseutils
+
+import ./math/vector
+
+func RGBfromHex*(value: string): Vec3 =
+  assert value != ""
+  var hex = value
+  if hex[0] == '#':
+    hex = hex[1 .. ^0]
+  assert hex.len == 3 or hex.len == 6
+  if hex.len == 3:
+    hex = hex[0] & hex[0] & hex[1] & hex[1] & hex[2] & hex[2]
+  var r, g, b: uint8
+  assert hex.len == 6
+  assert parseHex(hex[0 .. 1], r) == 2
+  assert parseHex(hex[2 .. 3], g) == 2
+  assert parseHex(hex[4 .. 5], b) == 2
+  return Vec3([float32(r), float32(g), float32(b)]) / 255'f
+
+func RGBAfromHex*(value: string): Vec4 =
+  assert value != ""
+  var hex = value
+  if hex[0] == '#':
+    hex = hex[1 .. ^0]
+  # when 3 or 6 -> set alpha to 1.0
+  assert hex.len == 3 or hex.len == 6 or hex.len == 4 or hex.len == 8
+  if hex.len == 3:
+    hex = hex & "f"
+  if hex.len == 4:
+    hex = hex[0] & hex[0] & hex[1] & hex[1] & hex[2] & hex[2] & hex[3] & hex[3]
+  if hex.len == 6:
+    hex = hex & "ff"
+  assert hex.len == 8
+  var r, g, b, a: uint8
+  assert parseHex(hex[0 .. 1], r) == 2
+  assert parseHex(hex[2 .. 3], g) == 2
+  assert parseHex(hex[4 .. 5], b) == 2
+  assert parseHex(hex[6 .. 7], a) == 2
+  return Vec4([float32(r), float32(g), float32(b), float32(a)]) / 255'f
+
+func gamma*[T: Vec3|Vec4](color: T, gamma: float32): auto =
+  return pow(color, gamma)
--- a/src/semicongine/engine.nim	Mon Feb 06 23:36:55 2023 +0700
+++ b/src/semicongine/engine.nim	Tue Feb 07 12:20:04 2023 +0700
@@ -95,7 +95,7 @@
     window*: NativeWindow
     currentscenedata*: Thing
     input*: Input
-    fps*: uint
+    maxFPS*: uint
 
 
 method update*(thing: Thing, engine: Engine, t, dt: float32) {.base.} = discard
@@ -810,8 +810,8 @@
 
 
 func frametime(engine: Engine): auto =
-  if engine.fps == 0: 0'f
-  else: 1'f / float32(engine.fps)
+  if engine.maxFPS == 0: 0'f
+  else: 1'f / float32(engine.maxFPS)
 
 proc run*(engine: var Engine, pipeline: var RenderPipeline, globalUpdate: proc(
     engine: var Engine, t, dt: float32)) =
@@ -863,11 +863,11 @@
       update(thing, engine, now, dt)
 
     # submit frame for drawing
-    if engine.fps == 0 or (now - lastframe >= engine.frametime): # framerate limit
+    if engine.maxFPS == 0 or (now - lastframe >= engine.frametime): # framerate limit
       engine.window.drawFrame(engine.vulkan, currentFrame, resized, pipeline)
       lastframe = now
+      currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT
     resized = false
-    currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT
 
   checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device)
 
--- a/src/semicongine/math/vector.nim	Mon Feb 06 23:36:55 2023 +0700
+++ b/src/semicongine/math/vector.nim	Tue Feb 07 12:20:04 2023 +0700
@@ -25,7 +25,8 @@
 # define some often used constants
 func ConstOne2[T: SomeNumber](): auto {.compiletime.} = TVec2[T]([T(1), T(1)])
 func ConstOne3[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(1), T(1), T(1)])
-func ConstOne4[T: SomeNumber](): auto {.compiletime.} = TVec4[T]([T(1), T(1), T(1), T(1)])
+func ConstOne4[T: SomeNumber](): auto {.compiletime.} = TVec4[T]([T(1), T(1), T(
+    1), T(1)])
 func ConstX[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(1), T(0), T(0)])
 func ConstY[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(1), T(0)])
 func ConstZ[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(0), T(1)])
@@ -39,7 +40,8 @@
 macro generateAllConsts() =
   result = newStmtList()
   for component in ["X", "Y", "Z", "R", "G", "B", "One2", "One3", "One4"]:
-    for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]:
+    for theType in ["int", "int8", "int16", "int32", "int64", "float",
+        "float32", "float64"]:
       var typename = theType[0 .. 0]
       if theType[^2].isDigit:
         typename = typename & theType[^2]
@@ -80,11 +82,16 @@
 func `$`*(v: TVec4[SomeNumber]): string = toString[TVec4[SomeNumber]](v)
 
 func length*(vec: TVec2[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1])
-func length*(vec: TVec2[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1]))
-func length*(vec: TVec3[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])
-func length*(vec: TVec3[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]))
-func length*(vec: TVec4[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3])
-func length*(vec: TVec4[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3]))
+func length*(vec: TVec2[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[
+    1] * vec[1]))
+func length*(vec: TVec3[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[
+    1] + vec[2] * vec[2])
+func length*(vec: TVec3[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[
+    1] * vec[1] + vec[2] * vec[2]))
+func length*(vec: TVec4[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[
+    1] + vec[2] * vec[2] + vec[3] * vec[3])
+func length*(vec: TVec4[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[
+    1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3]))
 
 func normalized*[T](vec: TVec2[T]): auto =
   let l = vec.length
@@ -103,62 +110,93 @@
   when T is SomeFloat:
     TVec4[T]([vec[0] / l, vec[1] / l, vec[2] / l, vec[3] / l])
   else:
-    TVec4[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l, float(vec[3]) / l])
+    TVec4[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l,
+        float(vec[3]) / l])
 
 # scalar operations
 func `+`*(a: TVec2, b: SomeNumber): auto = TVec2([a[0] + b, a[1] + b])
 func `+`*(a: TVec3, b: SomeNumber): auto = TVec3([a[0] + b, a[1] + b, a[2] + b])
-func `+`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b])
+func `+`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] + b, a[1] + b, a[2] + b,
+    a[3] + b])
 func `-`*(a: TVec2, b: SomeNumber): auto = TVec2([a[0] - b, a[1] - b])
 func `-`*(a: TVec3, b: SomeNumber): auto = TVec3([a[0] - b, a[1] - b, a[2] - b])
-func `-`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b])
+func `-`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] - b, a[1] - b, a[2] - b,
+    a[3] - b])
 func `*`*(a: TVec2, b: SomeNumber): auto = TVec2([a[0] * b, a[1] * b])
 func `*`*(a: TVec3, b: SomeNumber): auto = TVec3([a[0] * b, a[1] * b, a[2] * b])
-func `*`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b])
-func `/`*[T: SomeInteger](a: TVec2[T], b: SomeInteger): auto = TVec2([a[0] div b, a[1] div b])
+func `*`*(a: TVec4, b: SomeNumber): auto = TVec4([a[0] * b, a[1] * b, a[2] * b,
+    a[3] * b])
+func `/`*[T: SomeInteger](a: TVec2[T], b: SomeInteger): auto = TVec2([a[
+    0] div b, a[1] div b])
 func `/`*[T: SomeFloat](a: TVec2[T], b: SomeFloat): auto = TVec2([a[0] / b, a[1] / b])
-func `/`*[T: SomeInteger](a: TVec3[T], b: SomeInteger): auto = TVec3([a[0] div b, a[1] div b, a[2] div b])
-func `/`*[T: SomeFloat](a: TVec3[T], b: SomeFloat): auto = TVec3([a[0] / b, a[1] / b, a[2] / b])
-func `/`*[T: SomeInteger](a: TVec4[T], b: SomeInteger): auto = TVec4([a[0] div b, a[1] div b, a[2] div b, a[3] div b])
-func `/`*[T: SomeFloat](a: TVec4[T], b: SomeFloat): auto = TVec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b])
+func `/`*[T: SomeInteger](a: TVec3[T], b: SomeInteger): auto = TVec3([a[
+    0] div b, a[1] div b, a[2] div b])
+func `/`*[T: SomeFloat](a: TVec3[T], b: SomeFloat): auto = TVec3([a[0] / b, a[
+    1] / b, a[2] / b])
+func `/`*[T: SomeInteger](a: TVec4[T], b: SomeInteger): auto = TVec4([a[
+    0] div b, a[1] div b, a[2] div b, a[3] div b])
+func `/`*[T: SomeFloat](a: TVec4[T], b: SomeFloat): auto = TVec4([a[0] / b, a[
+    1] / b, a[2] / b, a[3] / b])
 
 func `+`*(a: SomeNumber, b: TVec2): auto = TVec2([a + b[0], a + b[1]])
 func `+`*(a: SomeNumber, b: TVec3): auto = TVec3([a + b[0], a + b[1], a + b[2]])
-func `+`*(a: SomeNumber, b: TVec4): auto = TVec4([a + b[0], a + b[1], a + b[2], a + b[3]])
+func `+`*(a: SomeNumber, b: TVec4): auto = TVec4([a + b[0], a + b[1], a + b[2],
+    a + b[3]])
 func `-`*(a: SomeNumber, b: TVec2): auto = TVec2([a - b[0], a - b[1]])
 func `-`*(a: SomeNumber, b: TVec3): auto = TVec3([a - b[0], a - b[1], a - b[2]])
-func `-`*(a: SomeNumber, b: TVec4): auto = TVec4([a - b[0], a - b[1], a - b[2], a - b[3]])
+func `-`*(a: SomeNumber, b: TVec4): auto = TVec4([a - b[0], a - b[1], a - b[2],
+    a - b[3]])
 func `*`*(a: SomeNumber, b: TVec2): auto = TVec2([a * b[0], a * b[1]])
 func `*`*(a: SomeNumber, b: TVec3): auto = TVec3([a * b[0], a * b[1], a * b[2]])
-func `*`*(a: SomeNumber, b: TVec4): auto = TVec4([a * b[0], a * b[1], a * b[2], a * b[3]])
-func `/`*[T: SomeInteger](a: SomeInteger, b: TVec2[T]): auto = TVec2([a div b[0], a div b[1]])
+func `*`*(a: SomeNumber, b: TVec4): auto = TVec4([a * b[0], a * b[1], a * b[2],
+    a * b[3]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: TVec2[T]): auto = TVec2([a div b[
+    0], a div b[1]])
 func `/`*[T: SomeFloat](a: SomeFloat, b: TVec2[T]): auto = TVec2([a / b[0], a / b[1]])
-func `/`*[T: SomeInteger](a: SomeInteger, b: TVec3[T]): auto = TVec3([a div b[0], a div b[1], a div b[2]])
-func `/`*[T: SomeFloat](a: SomeFloat, b: TVec3[T]): auto = TVec3([a / b[0], a / b[1], a / b[2]])
-func `/`*[T: SomeInteger](a: SomeInteger, b: TVec4[T]): auto = TVec4([a div b[0], a div b[1], a div b[2], a div b[3]])
-func `/`*[T: SomeFloat](a: SomeFloat, b: TVec4[T]): auto = TVec4([a / b[0], a / b[1], a / b[2], a / b[3]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: TVec3[T]): auto = TVec3([a div b[
+    0], a div b[1], a div b[2]])
+func `/`*[T: SomeFloat](a: SomeFloat, b: TVec3[T]): auto = TVec3([a / b[0], a /
+    b[1], a / b[2]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: TVec4[T]): auto = TVec4([a div b[
+    0], a div b[1], a div b[2], a div b[3]])
+func `/`*[T: SomeFloat](a: SomeFloat, b: TVec4[T]): auto = TVec4([a / b[0], a /
+    b[1], a / b[2], a / b[3]])
 
 # compontent-wise operations
 func `+`*(a, b: TVec2): auto = TVec2([a[0] + b[0], a[1] + b[1]])
 func `+`*(a, b: TVec3): auto = TVec3([a[0] + b[0], a[1] + b[1], a[2] + b[2]])
-func `+`*(a, b: TVec4): auto = TVec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]])
+func `+`*(a, b: TVec4): auto = TVec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[
+    3] + b[3]])
 func `-`*(a: TVec2): auto = TVec2([-a[0], -a[1]])
 func `-`*(a: TVec3): auto = TVec3([-a[0], -a[1], -a[2]])
 func `-`*(a: TVec4): auto = TVec4([-a[0], -a[1], -a[2], -a[3]])
 func `-`*(a, b: TVec2): auto = TVec2([a[0] - b[0], a[1] - b[1]])
 func `-`*(a, b: TVec3): auto = TVec3([a[0] - b[0], a[1] - b[1], a[2] - b[2]])
-func `-`*(a, b: TVec4): auto = TVec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]])
+func `-`*(a, b: TVec4): auto = TVec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[
+    3] - b[3]])
 func `*`*(a, b: TVec2): auto = TVec2([a[0] * b[0], a[1] * b[1]])
 func `*`*(a, b: TVec3): auto = TVec3([a[0] * b[0], a[1] * b[1], a[2] * b[2]])
-func `*`*(a, b: TVec4): auto = TVec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]])
-func `/`*[T: SomeInteger](a, b: TVec2[T]): auto = TVec2([a[0] div b[0], a[1] div b[1]])
+func `*`*(a, b: TVec4): auto = TVec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[
+    3] * b[3]])
+func `/`*[T: SomeInteger](a, b: TVec2[T]): auto = TVec2([a[0] div b[0], a[
+    1] div b[1]])
 func `/`*[T: SomeFloat](a, b: TVec2[T]): auto = TVec2([a[0] / b[0], a[1] / b[1]])
-func `/`*[T: SomeInteger](a, b: TVec3[T]): auto = TVec3([a[0] div b[0], a[1] div b[1], a[2] div b[2]])
-func `/`*[T: SomeFloat](a, b: TVec3[T]): auto = TVec3([a[0] / b[0], a[1] / b[1], a[2] / b[2]])
-func `/`*[T: SomeInteger](a, b: TVec4[T]): auto = TVec4([a[0] div b[0], a[1] div b[1], a[2] div b[2], a[3] div b[3]])
-func `/`*[T: SomeFloat](a, b: TVec4[T]): auto = TVec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]])
+func `/`*[T: SomeInteger](a, b: TVec3[T]): auto = TVec3([a[0] div b[0], a[
+    1] div b[1], a[2] div b[2]])
+func `/`*[T: SomeFloat](a, b: TVec3[T]): auto = TVec3([a[0] / b[0], a[1] / b[1],
+    a[2] / b[2]])
+func `/`*[T: SomeInteger](a, b: TVec4[T]): auto = TVec4([a[0] div b[0], a[
+    1] div b[1], a[2] div b[2], a[3] div b[3]])
+func `/`*[T: SomeFloat](a, b: TVec4[T]): auto = TVec4([a[0] / b[0], a[1] / b[1],
+    a[2] / b[2], a[3] / b[3]])
 
 # special operations
+func pow*(a: TVec2, b: SomeNumber): auto =
+  TVec2([pow(a[0], b), pow(a[1], b)])
+func pow*(a: TVec3, b: SomeNumber): auto =
+  TVec3([pow(a[0], b), pow(a[1], b), pow(a[2], b)])
+func pow*(a: TVec4, b: SomeNumber): auto =
+  TVec4([pow(a[0], b), pow(a[1], b), pow(a[2], b), pow(a[3], b)])
 func dot*(a, b: TVec2): auto = a[0] * b[0] + a[1] * b[1]
 func dot*(a, b: TVec3): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
 func dot*(a, b: TVec4): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
@@ -189,7 +227,8 @@
   if accessorvalue.len == 0:
     raise newException(Exception, "empty attribute")
   elif accessorvalue.len == 1:
-    ret = nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]]))
+    ret = nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[
+        accessorvalue[0]]))
   if accessorvalue.len > 1:
     var attrs = nnkBracket.newTree()
     for attrname in accessorvalue:
@@ -197,9 +236,10 @@
     ret = nnkCall.newTree(ident("TVec" & $accessorvalue.len), attrs)
 
   newProc(
-    name=nnkPostfix.newTree(ident("*"), ident(accessor)),
-    params=[ident("auto"), nnkIdentDefs.newTree(ident("value"), ident("TVec"), newEmptyNode())],
-    body=newStmtList(ret),
+    name = nnkPostfix.newTree(ident("*"), ident(accessor)),
+    params = [ident("auto"), nnkIdentDefs.newTree(ident("value"), ident("TVec"),
+        newEmptyNode())],
+    body = newStmtList(ret),
     procType = nnkFuncDef,
   )
 
@@ -221,12 +261,12 @@
 
 # call e.g. Vec2[int]().randomized() to get a random matrix
 template makeRandomInit(mattype: typedesc) =
-    proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] =
-      for i in 0 ..< result.len:
-        result[i] = rand(low(typeof(m[0])) .. high(typeof(m[0])))
-    proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] =
-      for i in 0 ..< result.len:
-        result[i] = rand(1.0)
+  proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] =
+    for i in 0 ..< result.len:
+      result[i] = rand(low(typeof(m[0])) .. high(typeof(m[0])))
+  proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] =
+    for i in 0 ..< result.len:
+      result[i] = rand(1.0)
 
 makeRandomInit(TVec2)
 makeRandomInit(TVec3)