changeset 247:beb41c93aa3f

fix: gltf loading
author Sam <sam@basx.dev>
date Tue, 23 May 2023 01:05:06 +0700
parents dcbd9f256f6a
children 952428f04ffc
files examples/E04_input.nim src/semicongine.nim src/semicongine/engine.nim src/semicongine/entity.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/resources.nim src/semicongine/resources/mesh.nim src/semicongine/scene.nim tests/test_mesh.nim
diffstat 10 files changed, 243 insertions(+), 213 deletions(-) [+]
line wrap: on
line diff
--- a/examples/E04_input.nim	Mon May 22 19:27:17 2023 +0700
+++ b/examples/E04_input.nim	Tue May 23 01:05:06 2023 +0700
@@ -179,10 +179,10 @@
   while myengine.updateInputs() == Running:
     if myengine.windowWasResized():
       setShaderGlobal(scene, "projection",
-        ortho[float32](
-          0'f32, float32(myengine.getWindow().size[0]),
-          0'f32, float32(myengine.getWindow().size[1]),
-          0'f32, 1'f32,
+        ortho(
+          0, float32(myengine.getWindow().size[0]),
+          0, float32(myengine.getWindow().size[1]),
+          0, 1,
         )
       )
       let
--- a/src/semicongine.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine.nim	Tue May 23 01:05:06 2023 +0700
@@ -3,7 +3,7 @@
 
 import semicongine/audio
 import semicongine/engine
-import semicongine/entity
+import semicongine/scene
 import semicongine/events
 import semicongine/mesh
 import semicongine/renderer
@@ -14,7 +14,7 @@
 
 export audio
 export engine
-export entity
+export scene
 export events
 export mesh
 export renderer
--- a/src/semicongine/engine.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine/engine.nim	Tue May 23 01:05:06 2023 +0700
@@ -11,7 +11,7 @@
 import ./vulkan/physicaldevice
 import ./vulkan/renderpass
 
-import ./entity
+import ./scene
 import ./renderer
 import ./events
 import ./audio
--- a/src/semicongine/entity.nim	Mon May 22 19:27:17 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-import std/strformat
-import std/tables
-import std/hashes
-import std/typetraits
-
-import ./core
-
-type
-  Component* = ref object of RootObj
-    entity*: Entity
-
-  Scene* = object
-    name*: string
-    root*: Entity
-    shaderGlobals*: Table[string, DataList]
-    textures*: Table[string, seq[Texture]]
-
-  Entity* = ref object of RootObj
-    name*: string
-    transform*: Mat4 # todo: cache transform + only update VBO when transform changed
-    parent*: Entity
-    children*: seq[Entity]
-    components*: seq[Component]
-
-func addShaderGlobal*[T](scene: var Scene, name: string, data: T) =
-  scene.shaderGlobals[name] = newDataList(thetype=getDataType[T]())
-  setValues(scene.shaderGlobals[name], @[data])
-
-func addShaderGlobalArray*[T](scene: var Scene, name: string, data: seq[T]) =
-  scene.shaderGlobals[name] = newDataList(thetype=getDataType[T]())
-  setValues(scene.shaderGlobals[name], data)
-
-func getShaderGlobal*[T](scene: Scene, name: string): T =
-  getValues[T](scene.shaderGlobals[name])[0]
-
-func getShaderGlobalArray*[T](scene: Scene, name: string): seq[T] =
-  getValues[T](scene.shaderGlobals[name])
-
-func setShaderGlobal*[T](scene: var Scene, name: string, value: T) =
-  setValues[T](scene.shaderGlobals[name], @[value])
-
-func setShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) =
-  setValues[T](scene.shaderGlobals[name], value)
-
-func addTextures*(scene: var Scene, name: string, textures: seq[Texture], interpolation=VK_FILTER_LINEAR) =
-  scene.textures[name] = textures
-
-func addTexture*(scene: var Scene, name: string, texture: Texture) =
-  scene.textures[name] = @[texture]
-
-func newScene*(name: string, root: Entity): Scene =
-  Scene(name: name, root: root)
-
-func hash*(scene: Scene): Hash =
-  hash(scene.name)
-
-func `==`*(a, b: Scene): bool =
-  a.name == b.name
-
-func hash*(entity: Entity): Hash =
-  hash(cast[pointer](entity))
-
-func hash*(component: Component): Hash =
-  hash(cast[pointer](component))
-
-method `$`*(entity: Entity): string {.base.} = entity.name
-method `$`*(component: Component): string {.base.} =
-  "Unknown Component"
-
-proc add*(entity: Entity, child: Entity) =
-  child.parent = entity
-  entity.children.add child
-proc add*(entity: Entity, component: Component) =
-  component.entity = entity
-  entity.components.add component
-proc add*(entity: Entity, children: seq[Entity]) =
-  for child in children:
-    child.parent = entity
-    entity.children.add child
-proc add*(entity: Entity, components: seq[Component]) =
-  for component in components:
-    component.entity = entity
-    entity.components.add component
-
-func newEntity*(name: string = ""): Entity =
-  result = new Entity
-  result.name = name
-  result.transform = Unit4
-  if result.name == "":
-    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
-
-func newEntity*(name: string, firstChild: Entity, children: varargs[Entity]): Entity =
-  result = new Entity
-  result.add firstChild
-  for child in children:
-    result.add child
-  result.name = name
-  result.transform = Unit4
-  if result.name == "":
-    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
-
-proc newEntity*(name: string, firstComponent: Component, components: varargs[Component]): Entity =
-  result = new Entity
-  result.name = name
-  result.add firstComponent
-  for component in components:
-    result.add component
-  if result.name == "":
-    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
-  result.transform = Unit4
-
-func getModelTransform*(entity: Entity): Mat4 =
-  result = Unit4
-  var currentEntity = entity
-  while currentEntity != nil:
-    result = currentEntity.transform * result
-    currentEntity = currentEntity.parent
-
-iterator allComponentsOfType*[T: Component](root: Entity): var T =
-  var queue = @[root]
-  while queue.len > 0:
-    let entity = queue.pop
-    for component in entity.components.mitems:
-      if component of T:
-        yield T(component)
-    for i in countdown(entity.children.len - 1, 0):
-      queue.add entity.children[i]
-
-func firstWithName*(root: Entity, name: string): Entity =
-  var queue = @[root]
-  while queue.len > 0:
-    let next = queue.pop
-    for child in next.children:
-      if child.name == name:
-        return child
-      queue.add child
-
-func firstComponentWithName*[T: Component](root: Entity, name: string): T =
-  var queue = @[root]
-  while queue.len > 0:
-    let next = queue.pop
-    for child in next.children:
-      if child.name == name:
-        for component in child.components:
-          if component of T:
-            return T(component)
-      queue.add child
-
-func allWithName*(root: Entity, name: string): seq[Entity] =
-  var queue = @[root]
-  while queue.len > 0:
-    let next = queue.pop
-    for child in next.children:
-      if child.name == name:
-        result.add child
-      queue.add child
-
-func allComponentsWithName*[T: Component](root: Entity, name: string): seq[T] =
-  var queue = @[root]
-  while queue.len > 0:
-    let next = queue.pop
-    for child in next.children:
-      if child.name == name:
-        for component in child.components:
-          if component of T:
-            result.add T(component)
-      queue.add child
-
-iterator allEntities*(root: Entity): Entity =
-  var queue = @[root]
-  while queue.len > 0:
-    let next = queue.pop
-    for child in next.children:
-      queue.add child
-    yield next
--- a/src/semicongine/mesh.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine/mesh.nim	Tue May 23 01:05:06 2023 +0700
@@ -6,7 +6,7 @@
 import std/sequtils
 
 import ./core
-import ./entity
+import ./scene
 
 type
   MeshIndexType* = enum
@@ -32,10 +32,9 @@
     of Big: VK_INDEX_TYPE_UINT32
 
 func vertexCount*(mesh: Mesh): uint32 =
-  if mesh.data.len == 0:
-    0'u32
-  else:
-    uint32(mesh.data[mesh.data.keys().toSeq[0]].len)
+  result = 0'u32
+  for list in mesh.data.values:
+    result = max(list.len, result)
 
 func indicesCount*(mesh: Mesh): uint32 =
   (
--- a/src/semicongine/renderer.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine/renderer.nim	Tue May 23 01:05:06 2023 +0700
@@ -15,7 +15,7 @@
 import ./vulkan/descriptor
 import ./vulkan/image
 
-import ./entity
+import ./scene
 import ./mesh
 
 type
--- a/src/semicongine/resources.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine/resources.nim	Tue May 23 01:05:06 2023 +0700
@@ -7,7 +7,7 @@
 import ./resources/audio
 import ./resources/mesh
 
-from ./entity import Entity, Scene
+from ./scene import Entity, Scene
 
 export image
 export audio
--- a/src/semicongine/resources/mesh.nim	Mon May 22 19:27:17 2023 +0700
+++ b/src/semicongine/resources/mesh.nim	Tue May 23 01:05:06 2023 +0700
@@ -6,7 +6,7 @@
 import std/strformat
 import std/streams
 
-import ../entity
+import ../scene
 import ../mesh
 import ../core
 
@@ -201,7 +201,6 @@
     result.addPrimitive(root, primitive, mainBuffer)
 
   # gld uses +y up, but we (vulkan) don't 
-  transform[Vec3f](result, "position", scale3d(1'f32, -1'f32, 1'f32))
 
 proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8]): Entity =
   var name = "<Unknown>"
@@ -250,7 +249,9 @@
 proc loadScene(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8]): Scene =
   var rootEntity = newEntity("<root>")
   for nodeId in scenenode["nodes"]:
-    rootEntity.add loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer)
+    let node = loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer)
+    node.transform = node.transform * scale3d(1'f32, -1'f32, 1'f32)
+    rootEntity.add node
 
   newScene(scenenode["name"].getStr(), rootEntity)
 
@@ -402,13 +403,11 @@
     if emissiveFactor.len > 0: scene.addShaderGlobalArray("material_emissive_factor", emissiveFactor)
 
     # texture
-    #[
-    if colorTexture.len > 0: scene.addShaderGlobalArray("material_color_texture", colorTexture)
-    if metallicRoughnessTexture.len > 0: scene.addShaderGlobalArray("material_metallic_roughness_texture", metallicRoughnessTexture)
-    if normalTexture.len > 0: scene.addShaderGlobalArray("material_normal_texture", normalTexture)
-    if occlusionTexture.len > 0: scene.addShaderGlobalArray("material_occlusion_texture", occlusionTexture)
-    if emissiveTexture.len > 0: scene.addShaderGlobalArray("material_emissive_texture", emissiveTexture)
-    ]#
+    if colorTexture.len > 0: scene.addTextures("material_color_texture", colorTexture)
+    if metallicRoughnessTexture.len > 0: scene.addTextures("material_metallic_roughness_texture", metallicRoughnessTexture)
+    if normalTexture.len > 0: scene.addTextures("material_normal_texture", normalTexture)
+    if occlusionTexture.len > 0: scene.addTextures("material_occlusion_texture", occlusionTexture)
+    if emissiveTexture.len > 0: scene.addTextures("material_emissive_texture", emissiveTexture)
 
     result.add scene
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/scene.nim	Tue May 23 01:05:06 2023 +0700
@@ -0,0 +1,194 @@
+import std/strformat
+import std/strutils
+import std/tables
+import std/hashes
+import std/typetraits
+
+import ./core
+
+type
+  Component* = ref object of RootObj
+    entity*: Entity
+
+  Scene* = object
+    name*: string
+    root*: Entity
+    shaderGlobals*: Table[string, DataList]
+    textures*: Table[string, seq[Texture]]
+
+  Entity* = ref object of RootObj
+    name*: string
+    transform*: Mat4 # todo: cache transform + only update VBO when transform changed
+    parent*: Entity
+    children*: seq[Entity]
+    components*: seq[Component]
+
+func getModelTransform*(entity: Entity): Mat4 =
+  result = Unit4
+  var currentEntity = entity
+  while currentEntity != nil:
+    result = currentEntity.transform * result
+    currentEntity = currentEntity.parent
+
+func addShaderGlobal*[T](scene: var Scene, name: string, data: T) =
+  scene.shaderGlobals[name] = newDataList(thetype=getDataType[T]())
+  setValues(scene.shaderGlobals[name], @[data])
+
+func addShaderGlobalArray*[T](scene: var Scene, name: string, data: seq[T]) =
+  scene.shaderGlobals[name] = newDataList(thetype=getDataType[T]())
+  setValues(scene.shaderGlobals[name], data)
+
+func getShaderGlobal*[T](scene: Scene, name: string): T =
+  getValues[T](scene.shaderGlobals[name])[0]
+
+func getShaderGlobalArray*[T](scene: Scene, name: string): seq[T] =
+  getValues[T](scene.shaderGlobals[name])
+
+func setShaderGlobal*[T](scene: var Scene, name: string, value: T) =
+  setValues[T](scene.shaderGlobals[name], @[value])
+
+func setShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) =
+  setValues[T](scene.shaderGlobals[name], value)
+
+func addTextures*(scene: var Scene, name: string, textures: seq[Texture], interpolation=VK_FILTER_LINEAR) =
+  scene.textures[name] = textures
+
+func addTexture*(scene: var Scene, name: string, texture: Texture) =
+  scene.textures[name] = @[texture]
+
+func newScene*(name: string, root: Entity): Scene =
+  Scene(name: name, root: root)
+
+func hash*(scene: Scene): Hash =
+  hash(scene.name)
+
+func `==`*(a, b: Scene): bool =
+  a.name == b.name
+
+func hash*(entity: Entity): Hash =
+  hash(cast[pointer](entity))
+
+func hash*(component: Component): Hash =
+  hash(cast[pointer](component))
+
+method `$`*(entity: Entity): string {.base.} = entity.name
+method `$`*(component: Component): string {.base.} =
+  "Unknown Component"
+
+proc prettyRecursive*(entity: Entity): seq[string] =
+  var compList: seq[string]
+  for comp in entity.components:
+    compList.add $comp
+
+  var trans = entity.transform.col(3)
+  var pos = entity.getModelTransform().col(3)
+  result.add "- " & $entity & " [" & $trans.x & ", " & $trans.y & ", " & $trans.z & "] ->  [" & $pos.x & ", " & $pos.y & ", " & $pos.z & "]"
+  if compList.len > 0:
+    result.add "  [" & compList.join(", ") & "]"
+
+  for child in entity.children:
+    for childLine in child.prettyRecursive:
+      result.add "  " & childLine
+
+proc pretty*(entity: Entity): string =
+  entity.prettyRecursive.join("\n")
+
+proc add*(entity: Entity, child: Entity) =
+  child.parent = entity
+  entity.children.add child
+proc add*(entity: Entity, component: Component) =
+  component.entity = entity
+  entity.components.add component
+proc add*(entity: Entity, children: seq[Entity]) =
+  for child in children:
+    child.parent = entity
+    entity.children.add child
+proc add*(entity: Entity, components: seq[Component]) =
+  for component in components:
+    component.entity = entity
+    entity.components.add component
+
+func newEntity*(name: string = ""): Entity =
+  result = new Entity
+  result.name = name
+  result.transform = Unit4
+  if result.name == "":
+    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
+
+func newEntity*(name: string, firstChild: Entity, children: varargs[Entity]): Entity =
+  result = new Entity
+  result.add firstChild
+  for child in children:
+    result.add child
+  result.name = name
+  result.transform = Unit4
+  if result.name == "":
+    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
+
+proc newEntity*(name: string, firstComponent: Component, components: varargs[Component]): Entity =
+  result = new Entity
+  result.name = name
+  result.add firstComponent
+  for component in components:
+    result.add component
+  if result.name == "":
+    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
+  result.transform = Unit4
+
+iterator allComponentsOfType*[T: Component](root: Entity): var T =
+  var queue = @[root]
+  while queue.len > 0:
+    let entity = queue.pop
+    for component in entity.components.mitems:
+      if component of T:
+        yield T(component)
+    for i in countdown(entity.children.len - 1, 0):
+      queue.add entity.children[i]
+
+func firstWithName*(root: Entity, name: string): Entity =
+  var queue = @[root]
+  while queue.len > 0:
+    let next = queue.pop
+    for child in next.children:
+      if child.name == name:
+        return child
+      queue.add child
+
+func firstComponentWithName*[T: Component](root: Entity, name: string): T =
+  var queue = @[root]
+  while queue.len > 0:
+    let next = queue.pop
+    for child in next.children:
+      if child.name == name:
+        for component in child.components:
+          if component of T:
+            return T(component)
+      queue.add child
+
+func allWithName*(root: Entity, name: string): seq[Entity] =
+  var queue = @[root]
+  while queue.len > 0:
+    let next = queue.pop
+    for child in next.children:
+      if child.name == name:
+        result.add child
+      queue.add child
+
+func allComponentsWithName*[T: Component](root: Entity, name: string): seq[T] =
+  var queue = @[root]
+  while queue.len > 0:
+    let next = queue.pop
+    for child in next.children:
+      if child.name == name:
+        for component in child.components:
+          if component of T:
+            result.add T(component)
+      queue.add child
+
+iterator allEntities*(root: Entity): Entity =
+  var queue = @[root]
+  while queue.len > 0:
+    let next = queue.pop
+    for child in next.children:
+      queue.add child
+    yield next
--- a/tests/test_mesh.nim	Mon May 22 19:27:17 2023 +0700
+++ b/tests/test_mesh.nim	Tue May 23 01:05:06 2023 +0700
@@ -1,13 +1,19 @@
 import semicongine
 
 proc main() =
+  var ent1 = newEntity("hoho", rect())
+  var ent2 = newEntity("hehe", ent1)
+  var myScene = newScene("hi", ent2)
+  myScene.root.transform = translate3d(0.2'f32, 0'f32, 0'f32)
+  myScene.root.children[0].transform = translate3d(0'f32, 0.2'f32, 0'f32)
   var scenes = [
-    loadScene("default_cube.glb", "1"),
-    loadScene("default_cube1.glb", "3"),
-    loadScene("default_cube2.glb", "4"),
-    loadScene("flat.glb", "5"),
+    # loadScene("default_cube.glb", "1"),
+    # loadScene("default_cube1.glb", "3"),
+    # loadScene("default_cube2.glb", "4"),
+    # loadScene("flat.glb", "5"),
     loadScene("tutorialk-donat.glb", "6"),
-    loadScene("personv3.glb", "2"),
+    # myScene,
+    # loadScene("personv3.glb", "2"),
   ]
 
   var engine = initEngine("Test meshes")
@@ -15,37 +21,46 @@
     vertexInput = @[
       attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
       attr[uint8]("material", memoryPerformanceHint=PreferFastRead),
+      attr[Vec2f]("texcoord_0", memoryPerformanceHint=PreferFastRead),
+      attr[Mat4]("transform", memoryPerformanceHint=PreferFastWrite, perInstance=true),
     ]
-    vertexOutput = @[attr[Vec4f]("vertexColor")]
+    vertexOutput = @[attr[Vec4f]("vertexColor"), attr[Vec2f]("colorTexCoord")]
     fragOutput = @[attr[Vec4f]("color")]
     uniforms = @[
       attr[Mat4]("projection"),
       attr[Mat4]("view"),
-      attr[Mat4]("model"),
       attr[Vec4f]("material_color", arrayCount=16),
     ]
+    samplers = @[
+      attr[Sampler2DType]("material_color_texture", arrayCount=1),
+    ]
     vertexCode = compileGlslShader(
       stage=VK_SHADER_STAGE_VERTEX_BIT,
       inputs=vertexInput,
       outputs=vertexOutput,
       uniforms=uniforms,
-      main="""gl_Position =  Uniforms.projection * Uniforms.view * Uniforms.model * vec4(position, 1.0); vertexColor = Uniforms.material_color[material];"""
+      samplers=samplers,
+      main="""
+gl_Position =  vec4(position, 1.0) * (transform * Uniforms.view * Uniforms.projection);
+vertexColor = Uniforms.material_color[material];
+colorTexCoord = texcoord_0;
+"""
     )
     fragmentCode = compileGlslShader(
       stage=VK_SHADER_STAGE_FRAGMENT_BIT,
       inputs=vertexOutput,
       outputs=fragOutput,
       uniforms=uniforms,
-      main="""color = vertexColor;"""
+      samplers=samplers,
+      main="""color = texture(material_color_texture[0], colorTexCoord) * vertexColor;"""
     )
   engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode, clearColor=newVec4f(0, 0, 0, 1)))
   for scene in scenes.mitems:
-    engine.addScene(scene, vertexInput)
+    engine.addScene(scene, vertexInput, transformAttribute="transform")
     scene.addShaderGlobal("projection", Unit4)
     scene.addShaderGlobal("view", Unit4)
-    scene.addShaderGlobal("model", Unit4)
   var
-    size = 0.3'f32
+    size = 1'f32
     elevation = 0'f32
     azimut = 0'f32
     currentScene = 0
@@ -78,10 +93,8 @@
       "view",
        scale3d(size, size, size) * rotate3d(elevation, newVec3f(1, 0, 0)) * rotate3d(azimut, Yf32)
     )
-    scenes[currentScene].setShaderGlobal("model", Unit4f32)
     engine.renderScene(scenes[currentScene])
   engine.destroy()
 
-
 when isMainModule:
   main()