# HG changeset patch # User Sam # Date 1692898191 -25200 # Node ID 670ca93a358bb14d9a32b3b9caf6ed21935716b4 # Parent 956e9c7e36da402a9d51404070b456e0e02ec936 fix: many issues, better mesh-handling, still need to cope with different binding numbers when using different pipelines... diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/collision.nim --- a/src/semicongine/collision.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/collision.nim Fri Aug 25 00:29:51 2023 +0700 @@ -1,15 +1,15 @@ import std/sequtils import ./core -import ./scene const MAX_COLLISON_DETECTION_ITERATIONS = 20 const MAX_COLLISON_POINT_CALCULATION_ITERATIONS = 20 type - HitBox* = ref object of Component + HitBox* = object transform*: Mat4 - HitSphere* = ref object of Component + HitSphere* = object + transform*: Mat4 radius*: float32 func between(value, b1, b2: float32): bool = @@ -18,7 +18,7 @@ func contains*(hitbox: HitBox, x: Vec3f): bool = # from https://math.stackexchange.com/questions/1472049/check-if-a-point-is-inside-a-rectangular-shaped-area-3d let - t = hitbox.entity.getModelTransform() * hitbox.transform + t = hitbox.transform P1 = t * newVec3f(0, 0, 0) # origin P2 = t * Z P4 = t * X @@ -53,10 +53,10 @@ func findFurthestPoint(hitsphere: HitSphere, direction: Vec3f): Vec3f = let directionNormalizedToSphere = ((direction / direction.length) * hitsphere.radius) - return hitsphere.entity.getModelTransform() * directionNormalizedToSphere + return hitsphere.transform * directionNormalizedToSphere func findFurthestPoint(hitbox: HitBox, direction: Vec3f): Vec3f = - let transform = hitbox.entity.getModelTransform() * hitbox.transform + let transform = hitbox.transform return findFurthestPoint( [ transform * newVec3f(0, 0, 0), @@ -383,7 +383,7 @@ scaleY = (maxY - minY) scaleZ = (maxZ - minZ) - HitBox(transform: translate3d(minX, minY, minZ) * scale3d(scaleX, scaleY, scaleZ)) + HitBox(transform: translate(minX, minY, minZ) * scale(scaleX, scaleY, scaleZ)) func calculateHitsphere*(points: seq[Vec3f]): HitSphere = result = HitSphere() diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/core/gpu_data.nim --- a/src/semicongine/core/gpu_data.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/core/gpu_data.nim Fri Aug 25 00:29:51 2023 +0700 @@ -1,5 +1,6 @@ import std/strformat import std/tables +import std/hashes import ./vulkanapi import ./vector @@ -98,7 +99,7 @@ of Mat4F64: mat4f64: TMat4[float64] of Sampler2D: discard DataList* = object - len*: uint32 + len*: int case thetype*: DataType of Float32: float32: ref seq[float32] of Float64: float64: ref seq[float64] @@ -148,11 +149,105 @@ ShaderAttribute* = object name*: string thetype*: DataType - arrayCount*: uint32 + arrayCount*: int perInstance*: bool noInterpolation: bool memoryPerformanceHint*: MemoryPerformanceHint +func hash*(value: DataList): Hash = + case value.thetype + of Float32: hash(cast[pointer](value.float32)) + of Float64: hash(cast[pointer](value.float64)) + of Int8: hash(cast[pointer](value.int8)) + of Int16: hash(cast[pointer](value.int16)) + of Int32: hash(cast[pointer](value.int32)) + of Int64: hash(cast[pointer](value.int64)) + of UInt8: hash(cast[pointer](value.uint8)) + of UInt16: hash(cast[pointer](value.uint16)) + of UInt32: hash(cast[pointer](value.uint32)) + of UInt64: hash(cast[pointer](value.uint64)) + of Vec2I32: hash(cast[pointer](value.vec2i32)) + of Vec2I64: hash(cast[pointer](value.vec2i64)) + of Vec3I32: hash(cast[pointer](value.vec3i32)) + of Vec3I64: hash(cast[pointer](value.vec3i64)) + of Vec4I32: hash(cast[pointer](value.vec4i32)) + of Vec4I64: hash(cast[pointer](value.vec4i64)) + of Vec2U32: hash(cast[pointer](value.vec2u32)) + of Vec2U64: hash(cast[pointer](value.vec2u64)) + of Vec3U32: hash(cast[pointer](value.vec3u32)) + of Vec3U64: hash(cast[pointer](value.vec3u64)) + of Vec4U32: hash(cast[pointer](value.vec4u32)) + of Vec4U64: hash(cast[pointer](value.vec4u64)) + of Vec2F32: hash(cast[pointer](value.vec2f32)) + of Vec2F64: hash(cast[pointer](value.vec2f64)) + of Vec3F32: hash(cast[pointer](value.vec3f32)) + of Vec3F64: hash(cast[pointer](value.vec3f64)) + of Vec4F32: hash(cast[pointer](value.vec4f32)) + of Vec4F64: hash(cast[pointer](value.vec4f64)) + of Mat2F32: hash(cast[pointer](value.mat2f32)) + of Mat2F64: hash(cast[pointer](value.mat2f64)) + of Mat23F32: hash(cast[pointer](value.mat23f32)) + of Mat23F64: hash(cast[pointer](value.mat23f64)) + of Mat32F32: hash(cast[pointer](value.mat32f32)) + of Mat32F64: hash(cast[pointer](value.mat32f64)) + of Mat3F32: hash(cast[pointer](value.mat3f32)) + of Mat3F64: hash(cast[pointer](value.mat3f64)) + of Mat34F32: hash(cast[pointer](value.mat34f32)) + of Mat34F64: hash(cast[pointer](value.mat34f64)) + of Mat43F32: hash(cast[pointer](value.mat43f32)) + of Mat43F64: hash(cast[pointer](value.mat43f64)) + of Mat4F32: hash(cast[pointer](value.mat4f32)) + of Mat4F64: hash(cast[pointer](value.mat4f64)) + of Sampler2D: raise newException(Exception, "hash not defined for Sampler2D") + +func `==`*(a, b: DataList): bool = + if a.thetype != b.thetype: + return false + case a.thetype + of Float32: return a.float32 == b.float32 + of Float64: return a.float64 == b.float64 + of Int8: return a.int8 == b.int8 + of Int16: return a.int16 == b.int16 + of Int32: return a.int32 == b.int32 + of Int64: return a.int64 == b.int64 + of UInt8: return a.uint8 == b.uint8 + of UInt16: return a.uint16 == b.uint16 + of UInt32: return a.uint32 == b.uint32 + of UInt64: return a.uint64 == b.uint64 + of Vec2I32: return a.vec2i32 == b.vec2i32 + of Vec2I64: return a.vec2i64 == b.vec2i64 + of Vec3I32: return a.vec3i32 == b.vec3i32 + of Vec3I64: return a.vec3i64 == b.vec3i64 + of Vec4I32: return a.vec4i32 == b.vec4i32 + of Vec4I64: return a.vec4i64 == b.vec4i64 + of Vec2U32: return a.vec2u32 == b.vec2u32 + of Vec2U64: return a.vec2u64 == b.vec2u64 + of Vec3U32: return a.vec3u32 == b.vec3u32 + of Vec3U64: return a.vec3u64 == b.vec3u64 + of Vec4U32: return a.vec4u32 == b.vec4u32 + of Vec4U64: return a.vec4u64 == b.vec4u64 + of Vec2F32: return a.vec2f32 == b.vec2f32 + of Vec2F64: return a.vec2f64 == b.vec2f64 + of Vec3F32: return a.vec3f32 == b.vec3f32 + of Vec3F64: return a.vec3f64 == b.vec3f64 + of Vec4F32: return a.vec4f32 == b.vec4f32 + of Vec4F64: return a.vec4f64 == b.vec4f64 + of Mat2F32: return a.mat2f32 == b.mat2f32 + of Mat2F64: return a.mat2f64 == b.mat2f64 + of Mat23F32: return a.mat23f32 == b.mat23f32 + of Mat23F64: return a.mat23f64 == b.mat23f64 + of Mat32F32: return a.mat32f32 == b.mat32f32 + of Mat32F64: return a.mat32f64 == b.mat32f64 + of Mat3F32: return a.mat3f32 == b.mat3f32 + of Mat3F64: return a.mat3f64 == b.mat3f64 + of Mat34F32: return a.mat34f32 == b.mat34f32 + of Mat34F64: return a.mat34f64 == b.mat34f64 + of Mat43F32: return a.mat43f32 == b.mat43f32 + of Mat43F64: return a.mat43f64 == b.mat43f64 + of Mat4F32: return a.mat4f32 == b.mat4f32 + of Mat4F64: return a.mat4f64 == b.mat4f64 + of Sampler2D: raise newException(Exception, "'==' not defined for Sampler2D") + func vertexInputs*(attributes: seq[ShaderAttribute]): seq[ShaderAttribute] = for attr in attributes: if attr.perInstance == false: @@ -163,14 +258,14 @@ if attr.perInstance == false: result.add attr -func numberOfVertexInputAttributeDescriptors*(thetype: DataType): uint32 = +func numberOfVertexInputAttributeDescriptors*(thetype: DataType): int = case thetype: of Mat2F32, Mat2F64, Mat23F32, Mat23F64: 2 of Mat32F32, Mat32F64, Mat3F32, Mat3F64, Mat34F32, Mat34F64: 3 of Mat43F32, Mat43F64, Mat4F32, Mat4F64: 4 else: 1 -func size*(thetype: DataType): uint32 = +func size*(thetype: DataType): int = case thetype: of Float32: 4 of Float64: 8 @@ -216,7 +311,7 @@ of Mat4F64: 128 of Sampler2D: 0 -func size*(attribute: ShaderAttribute, perDescriptor=false): uint32 = +func size*(attribute: ShaderAttribute, perDescriptor=false): int = if perDescriptor: attribute.thetype.size div attribute.thetype.numberOfVertexInputAttributeDescriptors else: @@ -225,14 +320,14 @@ else: attribute.thetype.size * attribute.arrayCount -func size*(thetype: seq[ShaderAttribute]): uint32 = +func size*(thetype: seq[ShaderAttribute]): int = for attribute in thetype: result += attribute.size -func size*(value: DataValue): uint32 = +func size*(value: DataValue): int = value.thetype.size -func size*(value: DataList): uint32 = +func size*(value: DataList): int = value.thetype.size * value.len func getDataType*[T: GPUType|int|uint|float](): DataType = @@ -292,7 +387,7 @@ func attr*[T: GPUType]( name: string, perInstance=false, - arrayCount=0'u32, + arrayCount=0, noInterpolation=false, memoryPerformanceHint=PreferFastRead, ): auto = @@ -357,7 +452,7 @@ else: {.error: "Virtual datatype has no value" .} func setValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) = - value.len = uint32(data.len) + value.len = data.len when T is float32: value.float32[] = data elif T is float64: value.float64[] = data elif T is int8: value.int8[] = data @@ -519,7 +614,7 @@ elif T is TMat4[float64]: value.mat4f64 else: {. error: "Virtual datatype has no values" .} -func getRawData*(value: var DataValue): (pointer, uint32) = +func getRawData*(value: DataValue): (pointer, int) = result[1] = value.thetype.size case value.thetype of Float32: result[0] = addr value.float32 @@ -566,9 +661,9 @@ of Mat4F64: result[0] = addr value.mat4f64 of Sampler2D: result[0] = nil -func getRawData*(value: var DataList): (pointer, uint32) = +func getRawData*(value: DataList): (pointer, int) = if value.len == 0: - return (nil, 0'u32) + return (nil, 0) result[1] = value.thetype.size * value.len case value.thetype of Float32: result[0] = addr value.float32[][0] @@ -615,7 +710,7 @@ of Mat4F64: result[0] = addr value.mat4f64[][0] of Sampler2D: result[0] = nil -func initData*(value: var DataList, len: uint32) = +func initData*(value: var DataList, len: int) = value.len = len case value.thetype of Float32: value.float32[].setLen(len) @@ -714,7 +809,7 @@ else: {.error: "Virtual datatype has no value" .} func appendValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) = - value.len += uint32(data.len) + value.len += data.len when T is float32: value.float32[].add data elif T is float64: value.float64[].add data elif T is int8: value.int8[].add data @@ -861,7 +956,7 @@ of Mat4F64: value.mat4f64[].add data.mat4f64 else: raise newException(Exception, &"Unsupported data type for GPU data:" ) -func setValue*[T: GPUType|int|uint|float](value: var DataList, i: uint32, data: T) = +func setValue*[T: GPUType|int|uint|float](value: var DataList, i: int, data: T) = assert i < value.len when T is float32: value.float32[][i] = data elif T is float64: value.float64[][i] = data @@ -962,7 +1057,7 @@ TYPEMAP[thetype] # from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html -func nLocationSlots*(thetype: DataType): uint32 = +func nLocationSlots*(thetype: DataType): int = #[ single location: 16-bit scalar and vector types, and @@ -1063,7 +1158,7 @@ func glslInput*(group: openArray[ShaderAttribute]): seq[string] = if group.len == 0: return @[] - var i = 0'u32 + var i = 0 for attribute in group: assert attribute.arrayCount == 0, "arrays not supported for shader vertex attributes" let flat = if attribute.noInterpolation: "flat " else: "" @@ -1097,7 +1192,7 @@ func glslOutput*(group: openArray[ShaderAttribute]): seq[string] = if group.len == 0: return @[] - var i = 0'u32 + var i = 0 for attribute in group: assert attribute.arrayCount == 0, "arrays not supported for outputs" let flat = if attribute.noInterpolation: "flat " else: "" diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/core/imagetypes.nim --- a/src/semicongine/core/imagetypes.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/core/imagetypes.nim Fri Aug 25 00:29:51 2023 +0700 @@ -3,8 +3,8 @@ type Pixel* = array[4, uint8] ImageObject* = object - width*: uint32 - height*: uint32 + width*: int + height*: int imagedata*: seq[Pixel] Sampler* = object magnification*: VkFilter @@ -23,26 +23,26 @@ wrapModeT: VK_SAMPLER_ADDRESS_MODE_REPEAT, ) -proc `[]`*(image: Image, x, y: uint32): Pixel = +proc `[]`*(image: Image, x, y: int): Pixel = assert x < image.width assert y < image.height image[].imagedata[y * image.width + x] -proc `[]=`*(image: var Image, x, y: uint32, value: Pixel) = +proc `[]=`*(image: var Image, x, y: int, value: Pixel) = assert x < image.width assert y < image.height image[].imagedata[y * image.width + x] = value const EMPTYPIXEL = [0'u8, 0'u8, 0'u8, 0'u8] -proc newImage*(width, height: uint32, imagedata: seq[Pixel] = @[], fill=EMPTYPIXEL): Image = +proc newImage*(width, height: int, imagedata: seq[Pixel] = @[], fill=EMPTYPIXEL): Image = assert width > 0 and height > 0 - assert uint32(imagedata.len) == width * height or imagedata.len == 0 + assert imagedata.len == width * height or imagedata.len == 0 result = new Image result.imagedata = (if imagedata.len == 0: newSeq[Pixel](width * height) else: imagedata) - assert width * height == uint32(result.imagedata.len) + assert width * height == result.imagedata.len result.width = width result.height = height diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/core/matrix.nim --- a/src/semicongine/core/matrix.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/core/matrix.nim Fri Aug 25 00:29:51 2023 +0700 @@ -376,19 +376,21 @@ sin(angle), cos(angle), T(0), T(0), T(0), T(1), ]) -func translate3d*(x=0'f32, y=0'f32, z=0'f32): TMat4[float32] = Mat4(data: [ +func translate*(x=0'f32, y=0'f32, z=0'f32): TMat4[float32] = Mat4(data: [ 1'f32, 0'f32, 0'f32, x, 0'f32, 1'f32, 0'f32, y, 0'f32, 0'f32, 1'f32, z, 0'f32, 0'f32, 0'f32, 1'f32, ]) -func scale3d*(x=1'f32, y=1'f32, z=1'f32): Mat4 = Mat4(data: [ +func translate*[T: TVec3](v: T): TMat4[float32] = translate(v[0], v[1], v[2]) +func scale*(x=1'f32, y=1'f32, z=1'f32): Mat4 = Mat4(data: [ x, 0'f32, 0'f32, 0'f32, 0'f32, y, 0'f32, 0'f32, 0'f32, 0'f32, z, 0'f32, 0'f32, 0'f32, 0'f32, 1'f32, ]) -func rotate3d*(angle: float32, a: Vec3f): Mat4 = +func scale*[T: TVec3](v: T): TMat4[float32] = scale(v[0], v[1], v[2]) +func rotate*(angle: float32, a: Vec3f): Mat4 = let cosa = cos(angle) sina = sin(angle) diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/engine.nim --- a/src/semicongine/engine.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/engine.nim Fri Aug 25 00:29:51 2023 +0700 @@ -107,7 +107,7 @@ assert not engine.renderer.isSome engine.renderer = some(engine.device.initRenderer(shaders=shaders, clearColor=clearColor)) -proc addScene*(engine: var Engine, scene: Scene) = +proc addScene*(engine: var Engine, scene: var Scene) = assert engine.renderer.isSome engine.renderer.get.setupDrawableBuffers(scene) @@ -118,11 +118,6 @@ engine.renderer.get.updateUniformData(scene) engine.renderer.get.render(scene) -proc updateAnimations*(engine: var Engine, scene: var Scene, dt: float32) = - assert engine.state == Running - assert engine.renderer.isSome - engine.renderer.get.updateAnimations(scene, dt) - proc updateInputs*(engine: var Engine): EngineState = assert engine.state in [Starting, Running] diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/mesh.nim --- a/src/semicongine/mesh.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/mesh.nim Fri Aug 25 00:29:51 2023 +0700 @@ -8,7 +8,6 @@ import std/sequtils import ./core -import ./scene import ./collision type @@ -17,18 +16,17 @@ Tiny # up to 2^8 vertices # TODO: need to check and enable support for this Small # up to 2^16 vertices Big # up to 2^32 vertices - Mesh* = ref object of Component - vertexCount*: uint32 - instanceCount*: uint32 - instanceTransforms*: seq[Mat4] # this should not reside in instanceData["transform"], as we will use instanceData["transform"] to store the final transformation matrix (as derived from the scene-tree) - material*: Material + Mesh* = object + vertexCount*: int case indexType*: MeshIndexType of None: discard of Tiny: tinyIndices: seq[array[3, uint8]] of Small: smallIndices: seq[array[3, uint16]] of Big: bigIndices: seq[array[3, uint32]] - visible: bool = true - dirtyInstanceTransforms: bool + material*: Material + transform*: Mat4 = Unit4F32 + instanceTransforms*: seq[Mat4] + transformCache: seq[Mat4] vertexData: Table[string, DataList] instanceData: Table[string, DataList] dirtyAttributes: seq[string] @@ -38,27 +36,8 @@ constants*: Table[string, DataValue] textures*: Table[string, Texture] -proc hash*(material: Material): Hash = - hash(cast[int64](material)) - -converter toVulkan*(indexType: MeshIndexType): VkIndexType = - case indexType: - of None: VK_INDEX_TYPE_NONE_KHR - of Tiny: VK_INDEX_TYPE_UINT8_EXT - of Small: VK_INDEX_TYPE_UINT16 - of Big: VK_INDEX_TYPE_UINT32 - -func indicesCount*(mesh: Mesh): uint32 = - ( - case mesh.indexType - of None: 0'u32 - of Tiny: uint32(mesh.tinyIndices.len) - of Small: uint32(mesh.smallIndices.len) - of Big: uint32(mesh.bigIndices.len) - ) * 3 - -method `$`*(mesh: Mesh): string = - &"Mesh, vertexCount: {mesh.vertexCount}, vertexData: {mesh.vertexData.keys().toSeq()}, indexType: {mesh.indexType}" +func `$`*(mesh: Mesh): string = + &"Mesh(vertexCount: {mesh.vertexCount}, vertexData: {mesh.vertexData.keys().toSeq()}, instanceData: {mesh.instanceData.keys().toSeq()}, indexType: {mesh.indexType})" proc `$`*(material: Material): string = var constants: seq[string] @@ -69,37 +48,75 @@ textures.add &"{key}" return &"""{material.name} | Values: {constants.join(", ")} | Textures: {textures.join(", ")}""" -func prettyData*(mesh: Mesh): string = - for attr, data in mesh.vertexData.pairs: - result &= &"{attr}: {data}\n" - result &= (case mesh.indexType - of None: "" - of Tiny: &"indices: {mesh.tinyIndices}" - of Small: &"indices: {mesh.smallIndices}" - of Big: &"indices: {mesh.bigIndices}") +func vertexAttributes*(mesh: Mesh): seq[string] = + mesh.vertexData.keys.toSeq + +func instanceAttributes*(mesh: Mesh): seq[string] = + mesh.instanceData.keys.toSeq + +func attributes*(mesh: Mesh): seq[string] = + mesh.vertexAttributes & mesh.instanceAttributes + +func hash*(material: Material): Hash = + hash(cast[pointer](material)) + +func instanceCount*(mesh: Mesh): int = + mesh.instanceTransforms.len + +converter toVulkan*(indexType: MeshIndexType): VkIndexType = + case indexType: + of None: VK_INDEX_TYPE_NONE_KHR + of Tiny: VK_INDEX_TYPE_UINT8_EXT + of Small: VK_INDEX_TYPE_UINT16 + of Big: VK_INDEX_TYPE_UINT32 -proc setMeshData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = - assert not (attribute in mesh.vertexData) - mesh.vertexData[attribute] = newDataList(data) +func indicesCount*(mesh: Mesh): int = + ( + case mesh.indexType + of None: 0 + of Tiny: mesh.tinyIndices.len + of Small: mesh.smallIndices.len + of Big: mesh.bigIndices.len + ) * 3 -proc setMeshData*(mesh: Mesh, attribute: string, data: DataList) = - assert not (attribute in mesh.vertexData) - mesh.vertexData[attribute] = data +func initVertexAttribute*[T](mesh: var Mesh, attribute: string, value: seq[T]) = + assert not mesh.vertexData.contains(attribute) + mesh.vertexData[attribute] = newDataList(thetype=getDataType[T]()) + mesh.vertexData[attribute].initData(mesh.vertexCount) + mesh.vertexData[attribute].setValues(value) +func initVertexAttribute*[T](mesh: var Mesh, attribute: string, value: T) = + initVertexAttribute(mesh, attribute, newSeqWith(mesh.vertexCount, value)) +func initVertexAttribute*[T](mesh: var Mesh, attribute: string) = + initVertexAttribute(mesh=mesh, attribute=attribute, value=default(T)) +func initVertexAttribute*(mesh: var Mesh, attribute: string, datatype: DataType) = + assert not mesh.vertexData.contains(attribute) + mesh.vertexData[attribute] = newDataList(thetype=datatype) + mesh.vertexData[attribute].initData(mesh.vertexCount) -proc setInstanceData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = - assert uint32(data.len) == mesh.instanceCount - assert not (attribute in mesh.instanceData) - mesh.instanceData[attribute] = newDataList(data) +func initInstanceAttribute*[T](mesh: var Mesh, attribute: string, value: seq[T]) = + assert not mesh.instanceData.contains(attribute) + mesh.instanceData[attribute] = newDataList(thetype=getDataType[T]()) + mesh.instanceData[attribute].initData(mesh.instanceCount) + mesh.instanceData[attribute].setValues(value) +func initInstanceAttribute*[T](mesh: var Mesh, attribute: string, value: T) = + initInstanceAttribute(mesh, attribute, newSeqWith(mesh.instanceCount, value)) +func initInstanceAttribute*[T](mesh: var Mesh, attribute: string) = + initInstanceAttribute(mesh=mesh, attribute=attribute, value=default(T)) +func initInstanceAttribute*(mesh: var Mesh, attribute: string, datatype: DataType) = + assert not mesh.instanceData.contains(attribute) + mesh.instanceData[attribute] = newDataList(thetype=datatype) + mesh.instanceData[attribute].initData(mesh.instanceCount) func newMesh*( positions: openArray[Vec3f], - indices: openArray[array[3, uint32|int32|uint16|int16|int]], + indices: openArray[array[3, uint32|uint16|uint8]], colors: openArray[Vec4f]=[], uvs: openArray[Vec2f]=[], + transform: Mat4=Unit4F32, + instanceTransforms: openArray[Mat4]=[Unit4F32], material: Material=nil, - instanceCount=1'u32, - autoResize=true -): auto = + autoResize=true, +): Mesh = assert colors.len == 0 or colors.len == positions.len assert uvs.len == 0 or uvs.len == positions.len @@ -113,22 +130,22 @@ indexType = Small result = Mesh( - instanceCount: instanceCount, - instanceTransforms: newSeqWith(int(instanceCount), Unit4F32), indexType: indexType, - vertexCount: uint32(positions.len) + vertexCount: positions.len, + instanceTransforms: @instanceTransforms, + transform: transform, + material: material, ) - result.material = material - setMeshData(result, "position", positions.toSeq) - if colors.len > 0: setMeshData(result, "color", colors.toSeq) - if uvs.len > 0: setMeshData(result, "uv", uvs.toSeq) + result.initVertexAttribute("position", positions.toSeq) + if colors.len > 0: result.initVertexAttribute("color", colors.toSeq) + if uvs.len > 0: result.initVertexAttribute("uv", uvs.toSeq) # assert all indices are valid for i in indices: - assert uint32(i[0]) < result.vertexCount - assert uint32(i[1]) < result.vertexCount - assert uint32(i[2]) < result.vertexCount + assert int(i[0]) < result.vertexCount + assert int(i[1]) < result.vertexCount + assert int(i[2]) < result.vertexCount # cast index values to appropiate type if result.indexType == Tiny and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support @@ -140,31 +157,26 @@ elif result.indexType == Big: for i, tri in enumerate(indices): result.bigIndices.add [uint32(tri[0]), uint32(tri[1]), uint32(tri[2])] - setInstanceData(result, "transform", newSeqWith(int(instanceCount), Unit4F32)) func newMesh*( positions: openArray[Vec3f], colors: openArray[Vec4f]=[], uvs: openArray[Vec2f]=[], - instanceCount=1'u32, + transform: Mat4=Unit4F32, + instanceTransforms: openArray[Mat4]=[Unit4F32], material: Material=nil, -): auto = +): Mesh = newMesh( positions=positions, - indices=newSeq[array[3, int]](), + indices=newSeq[array[3, uint16]](), colors=colors, uvs=uvs, + transform=transform, + instanceTransforms=instanceTransforms, material=material, - instanceCount=instanceCount, ) -func vertexAttributes*(mesh: Mesh): seq[string] = - mesh.vertexData.keys.toSeq - -func instanceAttributes*(mesh: Mesh): seq[string] = - mesh.instanceData.keys.toSeq - -func attributeSize*(mesh: Mesh, attribute: string): uint32 = +func attributeSize*(mesh: Mesh, attribute: string): int = if mesh.vertexData.contains(attribute): mesh.vertexData[attribute].size elif mesh.instanceData.contains(attribute): @@ -180,35 +192,32 @@ else: raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") -func indexSize*(mesh: Mesh): uint32 = +func indexSize*(mesh: Mesh): int = case mesh.indexType - of None: 0'u32 - of Tiny: uint32(mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0))) - of Small: uint32(mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0))) - of Big: uint32(mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0))) + of None: 0 + of Tiny: mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0)) + of Small: mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0)) + of Big: mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0)) -func rawData[T: seq](value: var T): (pointer, uint32) = - (pointer(addr(value[0])), uint32(sizeof(get(genericParams(typeof(value)), 0)) * value.len)) +func rawData[T: seq](value: T): (pointer, int) = + (pointer(addr(value[0])), sizeof(get(genericParams(typeof(value)), 0)) * value.len) -func getRawIndexData*(mesh: Mesh): (pointer, uint32) = +func getRawIndexData*(mesh: Mesh): (pointer, int) = case mesh.indexType: of None: raise newException(Exception, "Trying to get index data for non-indexed mesh") of Tiny: rawData(mesh.tinyIndices) of Small: rawData(mesh.smallIndices) of Big: rawData(mesh.bigIndices) -func hasAttribute*(mesh: Mesh, attribute: string): bool = - mesh.vertexData.contains(attribute) or mesh.instanceData.contains(attribute) - -func getRawData*(mesh: Mesh, attribute: string): (pointer, uint32) = +func getRawData*(mesh: Mesh, attribute: string): (pointer, int) = if mesh.vertexData.contains(attribute): mesh.vertexData[attribute].getRawData() elif mesh.instanceData.contains(attribute): mesh.instanceData[attribute].getRawData() else: - (nil, 0) + raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") -proc getMeshData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string): ref seq[T] = +proc getAttribute*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string): ref seq[T] = if mesh.vertexData.contains(attribute): getValues[T](mesh.vertexData[attribute]) elif mesh.instanceData.contains(attribute): @@ -216,36 +225,18 @@ else: raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") -proc initAttribute*(mesh: Mesh, attribute: ShaderAttribute) = - if attribute.perInstance: - mesh.instanceData[attribute.name] = newDataList(thetype=attribute.thetype) - mesh.instanceData[attribute.name].initData(mesh.instanceCount) - else: - mesh.vertexData[attribute.name] = newDataList(thetype=attribute.thetype) - mesh.vertexData[attribute.name].initData(mesh.vertexCount) - -proc initAttribute*[T](mesh: Mesh, attribute: ShaderAttribute, value: T) = - if attribute.perInstance: - mesh.instanceData[attribute.name] = newDataList(thetype=attribute.thetype) - mesh.instanceData[attribute.name].initData(mesh.instanceCount) - mesh.instanceData[attribute.name].setValues(newSeqWith(int(mesh.instanceCount), value)) - else: - mesh.vertexData[attribute.name] = newDataList(thetype=attribute.thetype) - mesh.vertexData[attribute.name].initData(mesh.vertexCount) - mesh.instanceData[attribute.name].setValues(newSeqWith(int(mesh.vertexCount), value)) - -proc updateAttributeData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = +proc updateAttributeData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = if mesh.vertexData.contains(attribute): - assert data.len < mesh.vertexData[attribute].len + assert data.len == mesh.vertexCount setValues(mesh.vertexData[attribute], data) elif mesh.instanceData.contains(attribute): - assert data.len < mesh.instanceData[attribute].len + assert data.len == mesh.instanceCount setValues(mesh.instanceData[attribute], data) else: raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") mesh.dirtyAttributes.add attribute -proc updateAttributeData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, i: uint32, value: T) = +proc updateAttributeData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, i: int, value: T) = if mesh.vertexData.contains(attribute): assert i < mesh.vertexData[attribute].len setValue(mesh.vertexData[attribute], i, value) @@ -256,17 +247,7 @@ raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") mesh.dirtyAttributes.add attribute -proc updateInstanceData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = - assert uint32(data.len) == mesh.instanceCount - if mesh.vertexData.contains(attribute): - setValues(mesh.vertexData[attribute], data) - elif mesh.instanceData.contains(attribute): - setValues(mesh.instanceData[attribute], data) - else: - raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") - mesh.dirtyAttributes.add attribute - -proc appendAttributeData*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = +proc appendAttributeData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = if mesh.vertexData.contains(attribute): appendValues(mesh.vertexData[attribute], data) elif mesh.instanceData.contains(attribute): @@ -276,7 +257,7 @@ mesh.dirtyAttributes.add attribute # currently only used for loading from files, shouls -proc appendAttributeData*(mesh: Mesh, attribute: string, data: DataList) = +proc appendAttributeData*(mesh: var Mesh, attribute: string, data: DataList) = if mesh.vertexData.contains(attribute): assert data.thetype == mesh.vertexData[attribute].thetype appendValues(mesh.vertexData[attribute], data) @@ -287,17 +268,23 @@ raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") mesh.dirtyAttributes.add attribute -proc appendIndicesData*(mesh: Mesh, v1, v2, v3: uint32) = +proc appendIndicesData*(mesh: var Mesh, v1, v2, v3: int) = case mesh.indexType of None: raise newException(Exception, "Mesh does not support indexed data") of Tiny: mesh.tinyIndices.add([uint8(v1), uint8(v2), uint8(v3)]) of Small: mesh.smallIndices.add([uint16(v1), uint16(v2), uint16(v3)]) - of Big: mesh.bigIndices.add([v1, v2, v3]) + of Big: mesh.bigIndices.add([uint32(v1), uint32(v2), uint32(v3)]) -func hasDataChanged*(mesh: Mesh, attribute: string): bool = - attribute in mesh.dirtyAttributes +proc updateInstanceTransforms*(mesh: var Mesh, attribute: string) = + let currentTransforms = mesh.instanceTransforms.mapIt(mesh.transform * it) + if currentTransforms != mesh.transformCache: + mesh.updateAttributeData(attribute, currentTransforms) + mesh.transformCache = currentTransforms -proc clearDataChanged*(mesh: Mesh) = +func dirtyAttributes*(mesh: Mesh): seq[string] = + mesh.dirtyAttributes + +proc clearDirtyAttributes*(mesh: var Mesh) = mesh.dirtyAttributes = @[] proc transform*[T: GPUType](mesh: Mesh, attribute: string, transform: Mat4) = @@ -313,10 +300,9 @@ func rect*(width=1'f32, height=1'f32, color="ffffffff"): Mesh = result = Mesh( vertexCount: 4, - instanceCount: 1, + instanceTransforms: @[Unit4F32], indexType: Small, smallIndices: @[[0'u16, 1'u16, 2'u16], [2'u16, 3'u16, 0'u16]], - instanceTransforms: @[Unit4F32] ) let @@ -325,24 +311,23 @@ pos = @[newVec3f(-half_w, -half_h), newVec3f( half_w, -half_h), newVec3f( half_w, half_h), newVec3f(-half_w, half_h)] c = hexToColorAlpha(color) - setMeshData(result, "position", pos) - setMeshData(result, "color", @[c, c, c, c]) - setMeshData(result, "uv", @[newVec2f(0, 0), newVec2f(1, 0), newVec2f(1, 1), newVec2f(0, 1)]) - setInstanceData(result, "transform", @[Unit4F32]) + result.initVertexAttribute("position", pos) + result.initVertexAttribute("color", @[c, c, c, c]) + result.initVertexAttribute("uv", @[newVec2f(0, 0), newVec2f(1, 0), newVec2f(1, 1), newVec2f(0, 1)]) func tri*(width=1'f32, height=1'f32, color="ffffffff"): Mesh = - result = Mesh(vertexCount: 3, instanceCount: 1, instanceTransforms: @[Unit4F32]) + result = Mesh(vertexCount: 3, instanceTransforms: @[Unit4F32]) let half_w = width / 2 half_h = height / 2 colorVec = hexToColorAlpha(color) - setMeshData(result, "position", @[newVec3f(0, -half_h), newVec3f( half_w, half_h), newVec3f(-half_w, half_h)]) - setMeshData(result, "color", @[colorVec, colorVec, colorVec]) - setInstanceData(result, "transform", @[Unit4F32]) + + result.initVertexAttribute("position", @[newVec3f(0, -half_h), newVec3f( half_w, half_h), newVec3f(-half_w, half_h)]) + result.initVertexAttribute("color", @[colorVec, colorVec, colorVec]) -func circle*(width=1'f32, height=1'f32, nSegments=12'u16, color="ffffffff"): Mesh = +func circle*(width=1'f32, height=1'f32, nSegments=12, color="ffffffff"): Mesh = assert nSegments >= 3 - result = Mesh(vertexCount: 3 + nSegments, instanceCount: 1, indexType: Small, instanceTransforms: @[Unit4F32]) + result = Mesh(vertexCount: 3 + nSegments, instanceTransforms: @[Unit4F32], indexType: Small) let half_w = width / 2 @@ -352,36 +337,14 @@ var pos = @[newVec3f(0, 0), newVec3f(0, half_h)] col = @[c, c] - for i in 0'u16 .. nSegments: + for i in 0 .. nSegments: pos.add newVec3f(cos(float32(i) * step) * half_w, sin(float32(i) * step) * half_h) col.add c - result.smallIndices.add [0'u16, i + 1, i + 2] - - setMeshData(result, "position", pos) - setMeshData(result, "color", col) - setInstanceData(result, "transform", @[Unit4F32]) - -proc areInstanceTransformsDirty*(mesh: Mesh): bool = - result = mesh.dirtyInstanceTransforms - mesh.dirtyInstanceTransforms = false + result.smallIndices.add [uint16(0), uint16(i + 1), uint16(i + 2)] -proc setInstanceTransform*(mesh: Mesh, i: uint32, mat: Mat4) = - assert 0 <= i and i < mesh.instanceCount - mesh.instanceTransforms[i] = mat - mesh.dirtyInstanceTransforms = true - -proc setInstanceTransforms*(mesh: Mesh, mat: seq[Mat4]) = - mesh.instanceTransforms = mat - mesh.dirtyInstanceTransforms = true - -func getInstanceTransform*(mesh: Mesh, i: uint32): Mat4 = - assert 0 <= i and i < mesh.instanceCount - mesh.instanceTransforms[i] - -func getInstanceTransforms*(mesh: Mesh): seq[Mat4] = - mesh.instanceTransforms + result.initVertexAttribute("position", pos) + result.initVertexAttribute("color", col) func getCollisionPoints*(mesh: Mesh, positionAttribute="position"): seq[Vec3f] = - for p in getMeshData[Vec3f](mesh, positionAttribute)[]: - result.add mesh.entity.getModelTransform() * p - + for p in getAttribute[Vec3f](mesh, positionAttribute)[]: + result.add p diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/renderer.nim --- a/src/semicongine/renderer.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/renderer.nim Fri Aug 25 00:29:51 2023 +0700 @@ -1,8 +1,9 @@ import std/options import std/tables import std/strformat +import std/sequtils import std/logging -import std/sequtils +import std/enumerate import ./core import ./vulkan/buffer @@ -19,21 +20,21 @@ import ./scene import ./mesh +const MATERIALINDEXATTRIBUTE = "materialIndex" +const TRANSFORMATTRIBUTE = "transform" + type SceneData = object - drawables*: OrderedTable[Mesh, Drawable] + drawables*: seq[tuple[drawable: Drawable, meshIndex: int]] vertexBuffers*: Table[MemoryPerformanceHint, Buffer] indexBuffer*: Buffer uniformBuffers*: Table[VkPipeline, seq[Buffer]] # one per frame-in-flight textures*: Table[string, seq[VulkanTexture]] # per frame-in-flight attributeLocation*: Table[string, MemoryPerformanceHint] attributeBindingNumber*: Table[string, int] - transformAttribute: string # name of attribute that is used for per-instance mesh transformation - materialIndexAttribute: string # name of attribute that is used for material selection - materials: seq[Material] - entityTransformationCache: Table[Mesh, Mat4] # remembers last transformation, avoid to send GPU-updates if no changes descriptorPools*: Table[VkPipeline, DescriptorPool] descriptorSets*: Table[VkPipeline, seq[DescriptorSet]] + materials: seq[Material] Renderer* = object device: Device surfaceFormat: VkSurfaceFormatKHR @@ -42,11 +43,8 @@ scenedata: Table[Scene, SceneData] emptyTexture: VulkanTexture -func usesMaterialType(scenedata: SceneData, materialType: string): bool = - for drawable in scenedata.drawables.values: - if drawable.mesh.material.materialType == materialType: - return true - return false +func usesMaterialType(scene: Scene, materialType: string): bool = + return scene.meshes.anyIt(it.material.materialType == materialType) proc initRenderer*(device: Device, shaders: Table[string, ShaderConfiguration], clearColor=Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])): Renderer = assert device.vk.valid @@ -62,47 +60,34 @@ result.swapchain = swapchain.get() result.emptyTexture = device.uploadTexture(EMPTYTEXTURE) -func inputs(renderer: Renderer): seq[ShaderAttribute] = +func inputs(renderer: Renderer, scene: Scene): seq[ShaderAttribute] = + var found: Table[string, ShaderAttribute] for i in 0 ..< renderer.renderPass.subpasses.len: - for pipeline in renderer.renderPass.subpasses[i].pipelines.values: - result.add pipeline.inputs + for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs: + if scene.usesMaterialType(materialType): + for input in pipeline.inputs: + if found.contains(input.name): + assert input == found[input.name] + else: + result.add input + found[input.name] = input -func samplers(renderer: Renderer): seq[ShaderAttribute] = +func samplers(renderer: Renderer, scene: Scene): seq[ShaderAttribute] = for i in 0 ..< renderer.renderPass.subpasses.len: - for pipeline in renderer.renderPass.subpasses[i].pipelines.values: - result.add pipeline.samplers + for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs: + if scene.usesMaterialType(materialType): + result.add pipeline.samplers -proc setupDrawableBuffers*(renderer: var Renderer, scene: Scene) = +proc setupDrawableBuffers*(renderer: var Renderer, scene: var Scene) = assert not (scene in renderer.scenedata) const VERTEX_ATTRIB_ALIGNMENT = 4 # used for buffer alignment let - inputs = renderer.inputs - samplers = renderer.samplers + inputs = renderer.inputs(scene) + samplers = renderer.samplers(scene) var scenedata = SceneData() - # if mesh transformation are handled through the scenegraph-transformation, set it up here - if scene.transformAttribute != "": - var hasTransformAttribute = false - for input in inputs: - if input.name == scene.transformAttribute: - assert input.perInstance == true, $input - assert getDataType[Mat4]() == input.thetype, $input - hasTransformAttribute = true - assert hasTransformAttribute - scenedata.transformAttribute = scene.transformAttribute - - # check if we have support for material indices, if required - if scene.materialIndexAttribute != "": - var hasMaterialIndexAttribute = false - for input in inputs: - if input.name == scene.materialIndexAttribute: - assert getDataType[uint16]() == input.thetype - hasMaterialIndexAttribute = true - assert hasMaterialIndexAttribute - scenedata.materialIndexAttribute = scene.materialIndexAttribute - - for mesh in allComponentsOfType[Mesh](scene.root): + for mesh in scene.meshes: if mesh.material != nil and not scenedata.materials.contains(mesh.material): scenedata.materials.add mesh.material for textureName, texture in mesh.material.textures.pairs: @@ -111,30 +96,33 @@ scenedata.textures[textureName].add renderer.device.uploadTexture(texture) # find all meshes, populate missing attribute values for shader - var allMeshes: seq[Mesh] - for mesh in allComponentsOfType[Mesh](scene.root): - allMeshes.add mesh + for mesh in scene.meshes.mitems: for inputAttr in inputs: - if scenedata.materialIndexAttribute != "" and inputAttr.name == scenedata.materialIndexAttribute: - assert mesh.material != nil, "Missing material specification for mesh. Either set the 'materials' attribute or pass the argument 'materialIndexAttribute=\"\"' when calling 'addScene'" + if inputAttr.name == TRANSFORMATTRIBUTE: + mesh.initInstanceAttribute(inputAttr.name, inputAttr.thetype) + elif inputAttr.name == MATERIALINDEXATTRIBUTE: + assert mesh.material != nil, "Missing material specification for mesh. Set material attribute on mesh" let matIndex = scenedata.materials.find(mesh.material) if matIndex < 0: raise newException(Exception, &"Required material '{mesh.material}' not available in scene (available are: {scenedata.materials})") - mesh.initAttribute(inputAttr, uint16(matIndex)) - elif not mesh.hasAttribute(inputAttr.name): + mesh.initInstanceAttribute(inputAttr.name, uint16(matIndex)) + elif not mesh.attributes.contains(inputAttr.name): warn(&"Mesh is missing data for shader attribute {inputAttr.name}, auto-filling with empty values") - mesh.initAttribute(inputAttr) + if inputAttr.perInstance: + mesh.initInstanceAttribute(inputAttr.name, inputAttr.thetype) + else: + mesh.initVertexAttribute(inputAttr.name, inputAttr.thetype) assert mesh.attributeType(inputAttr.name) == inputAttr.thetype, &"mesh attribute {inputAttr.name} has type {mesh.attributeType(inputAttr.name)} but shader expects {inputAttr.thetype}" # create index buffer if necessary - var indicesBufferSize = 0'u64 - for mesh in allMeshes: + var indicesBufferSize = 0 + for mesh in scene.meshes: if mesh.indexType != MeshIndexType.None: let indexAlignment = case mesh.indexType - of MeshIndexType.None: 0'u64 - of Tiny: 1'u64 - of Small: 2'u64 - of Big: 4'u64 + of MeshIndexType.None: 0 + of Tiny: 1 + of Small: 2 + of Big: 4 # index value alignment required by Vulkan if indicesBufferSize mod indexAlignment != 0: indicesBufferSize += indexAlignment - (indicesBufferSize mod indexAlignment) @@ -150,8 +138,8 @@ # create vertex buffers and calculcate offsets # trying to use one buffer per memory type var - perLocationOffsets: Table[MemoryPerformanceHint, uint64] - perLocationSizes: Table[MemoryPerformanceHint, uint64] + perLocationOffsets: Table[MemoryPerformanceHint, int] + perLocationSizes: Table[MemoryPerformanceHint, int] bindingNumber = 0 for hint in MemoryPerformanceHint: perLocationOffsets[hint] = 0 @@ -161,7 +149,7 @@ scenedata.attributeBindingNumber[attribute.name] = bindingNumber inc bindingNumber # setup one buffer per attribute-location-type - for mesh in allMeshes: + for mesh in scene.meshes: # align size to VERTEX_ATTRIB_ALIGNMENT bytes (the important thing is the correct alignment of the offsets, but # we need to expand the buffer size as well, therefore considering alignment already here as well if perLocationSizes[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT != 0: @@ -177,9 +165,9 @@ ) # fill vertex buffers - var indexBufferOffset = 0'u64 - for mesh in allMeshes: - var offsets: seq[(string, MemoryPerformanceHint, uint64)] + var indexBufferOffset = 0 + for (meshIndex, mesh) in enumerate(scene.meshes): + var offsets: seq[(string, MemoryPerformanceHint, int)] for attribute in inputs: offsets.add (attribute.name, attribute.memoryPerformanceHint, perLocationOffsets[attribute.memoryPerformanceHint]) var (pdata, size) = mesh.getRawData(attribute.name) @@ -191,7 +179,6 @@ let indexed = mesh.indexType != MeshIndexType.None var drawable = Drawable( - mesh: mesh, elementCount: if indexed: mesh.indicesCount else: mesh.vertexCount, bufferOffsets: offsets, instanceCount: mesh.instanceCount, @@ -199,10 +186,10 @@ ) if indexed: let indexAlignment = case mesh.indexType - of MeshIndexType.None: 0'u64 - of Tiny: 1'u64 - of Small: 2'u64 - of Big: 4'u64 + of MeshIndexType.None: 0 + of Tiny: 1 + of Small: 2 + of Big: 4 # index value alignment required by Vulkan if indexBufferOffset mod indexAlignment != 0: indexBufferOffset += indexAlignment - (indexBufferOffset mod indexAlignment) @@ -211,95 +198,75 @@ var (pdata, size) = mesh.getRawIndexData() scenedata.indexBuffer.setData(pdata, size, indexBufferOffset) indexBufferOffset += size - scenedata.drawables[mesh] = drawable + scenedata.drawables.add (drawable, meshIndex) # setup uniforms and samplers for subpass_i in 0 ..< renderer.renderPass.subpasses.len: - for material, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs: - var uniformBufferSize = 0'u64 - for uniform in pipeline.uniforms: - uniformBufferSize += uniform.size - if uniformBufferSize > 0: - scenedata.uniformBuffers[pipeline.vk] = newSeq[Buffer]() + for materialType, pipeline in renderer.renderPass.subpasses[subpass_i].pipelines.pairs: + if scene.usesMaterialType(materialType): + var uniformBufferSize = 0 + for uniform in pipeline.uniforms: + uniformBufferSize += uniform.size + if uniformBufferSize > 0: + scenedata.uniformBuffers[pipeline.vk] = newSeq[Buffer]() + for frame_i in 0 ..< renderer.swapchain.inFlightFrames: + scenedata.uniformBuffers[pipeline.vk].add renderer.device.createBuffer( + size=uniformBufferSize, + usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], + requireMappable=true, + preferVRAM=true, + ) + + var poolsizes = @[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, renderer.swapchain.inFlightFrames)] + if samplers.len > 0: + var samplercount = 0 + for sampler in samplers: + samplercount += (if sampler.arrayCount == 0: 1 else: sampler.arrayCount) + poolsizes.add (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, renderer.swapchain.inFlightFrames * samplercount * 2) + + scenedata.descriptorPools[pipeline.vk] = renderer.device.createDescriptorSetPool(poolsizes) + + scenedata.descriptorSets[pipeline.vk] = pipeline.setupDescriptors( + scenedata.descriptorPools[pipeline.vk], + scenedata.uniformBuffers.getOrDefault(pipeline.vk, @[]), + scenedata.textures, + inFlightFrames=renderer.swapchain.inFlightFrames, + emptyTexture=renderer.emptyTexture, + ) for frame_i in 0 ..< renderer.swapchain.inFlightFrames: - scenedata.uniformBuffers[pipeline.vk].add renderer.device.createBuffer( - size=uniformBufferSize, - usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], - requireMappable=true, - preferVRAM=true, - ) - - var poolsizes = @[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32(renderer.swapchain.inFlightFrames))] - if samplers.len > 0: - var samplercount = 0'u32 - for sampler in samplers: - samplercount += (if sampler.arrayCount == 0: 1'u32 else: sampler.arrayCount) - poolsizes.add (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32(renderer.swapchain.inFlightFrames) * samplercount * 2) - - scenedata.descriptorPools[pipeline.vk] = renderer.device.createDescriptorSetPool(poolsizes) - - scenedata.descriptorSets[pipeline.vk] = pipeline.setupDescriptors( - scenedata.descriptorPools[pipeline.vk], - scenedata.uniformBuffers.getOrDefault(pipeline.vk, @[]), - scenedata.textures, - inFlightFrames=renderer.swapchain.inFlightFrames, - emptyTexture=renderer.emptyTexture, - ) - for frame_i in 0 ..< renderer.swapchain.inFlightFrames: - scenedata.descriptorSets[pipeline.vk][frame_i].writeDescriptorSet() + scenedata.descriptorSets[pipeline.vk][frame_i].writeDescriptorSet() renderer.scenedata[scene] = scenedata -proc refreshMeshAttributeData(sceneData: var SceneData, mesh: Mesh, attribute: string) = - debug &"Refreshing data on mesh {mesh} for {attribute}" +proc refreshMeshAttributeData(renderer: Renderer, scene: Scene, drawable: Drawable, meshIndex: int, attribute: string) = + debug &"Refreshing data on mesh {scene.meshes[meshIndex]} for {attribute}" # ignore attributes that are not used in this shader - if not (attribute in sceneData.attributeLocation): + if not (attribute in renderer.scenedata[scene].attributeLocation): return - var (pdata, size) = mesh.getRawData(attribute) - let memoryPerformanceHint = sceneData.attributeLocation[attribute] - let bindingNumber = sceneData.attributeBindingNumber[attribute] - sceneData.vertexBuffers[memoryPerformanceHint].setData(pdata, size, sceneData.drawables[mesh].bufferOffsets[bindingNumber][2]) + var (pdata, size) = scene.meshes[meshIndex].getRawData(attribute) + let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute] + let bindingNumber = renderer.scenedata[scene].attributeBindingNumber[attribute] + renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(pdata, size, drawable.bufferOffsets[bindingNumber][2]) -proc updateMeshData*(renderer: var Renderer, scene: Scene) = +proc updateMeshData*(renderer: var Renderer, scene: var Scene) = assert scene in renderer.scenedata - for mesh in allComponentsOfType[Mesh](scene.root): - # if mesh transformation attribute is enabled, update the model matrix - if renderer.scenedata[scene].transformAttribute != "": - let transform = mesh.entity.getModelTransform() - if not (mesh in renderer.scenedata[scene].entityTransformationCache) or renderer.scenedata[scene].entityTransformationCache[mesh] != transform or mesh.areInstanceTransformsDirty : - var updatedTransform = newSeq[Mat4](int(mesh.instanceCount)) - for i in 0 ..< mesh.instanceCount: - updatedTransform[i] = transform * mesh.getInstanceTransform(i) - debug &"Update mesh transformation" - mesh.updateInstanceData(renderer.scenedata[scene].transformAttribute, updatedTransform) - renderer.scenedata[scene].entityTransformationCache[mesh] = transform - - # update any changed mesh attributes - for attribute in mesh.vertexAttributes: - if mesh.hasDataChanged(attribute): - renderer.scenedata[scene].refreshMeshAttributeData(mesh, attribute) - debug &"Update mesh vertex attribute {attribute}" - for attribute in mesh.instanceAttributes: - if mesh.hasDataChanged(attribute): - renderer.scenedata[scene].refreshMeshAttributeData(mesh, attribute) - debug &"Update mesh instance attribute {attribute}" - var m = mesh - m.clearDataChanged() - -proc updateAnimations*(renderer: var Renderer, scene: var Scene, dt: float32) = - for animation in allComponentsOfType[EntityAnimation](scene.root): - debug &"Update animation {animation}" - animation.update(dt) + for (drawable, meshIndex) in renderer.scenedata[scene].drawables.mitems: + if scene.meshes[meshIndex].attributes.contains(TRANSFORMATTRIBUTE): + scene.meshes[meshIndex].updateInstanceTransforms(TRANSFORMATTRIBUTE) + for attribute in scene.meshes[meshIndex].dirtyAttributes: + renderer.refreshMeshAttributeData(scene, drawable, meshIndex, attribute) + debug &"Update mesh attribute {attribute}" + scene.meshes[meshIndex].clearDirtyAttributes() proc updateUniformData*(renderer: var Renderer, scene: var Scene) = assert scene in renderer.scenedata for i in 0 ..< renderer.renderPass.subpasses.len: for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs: - if renderer.scenedata[scene].usesMaterialType(materialType) and renderer.scenedata[scene].uniformBuffers.hasKey(pipeline.vk) and renderer.scenedata[scene].uniformBuffers[pipeline.vk].len != 0: + if scene.usesMaterialType(materialType) and renderer.scenedata[scene].uniformBuffers.hasKey(pipeline.vk) and renderer.scenedata[scene].uniformBuffers[pipeline.vk].len != 0: assert renderer.scenedata[scene].uniformBuffers[pipeline.vk][renderer.swapchain.currentInFlight].vk.valid - var offset = 0'u64 + var offset = 0 for uniform in pipeline.uniforms: if not scene.shaderGlobals.hasKey(uniform.name): raise newException(Exception, &"Uniform '{uniform.name}' not found in scene shaderGlobals") @@ -310,7 +277,7 @@ renderer.scenedata[scene].uniformBuffers[pipeline.vk][renderer.swapchain.currentInFlight].setData(pdata, size, offset) offset += size -proc render*(renderer: var Renderer, scene: var Scene) = +proc render*(renderer: var Renderer, scene: Scene) = assert scene in renderer.scenedata var @@ -336,13 +303,13 @@ for i in 0 ..< renderer.renderPass.subpasses.len: for materialType, pipeline in renderer.renderPass.subpasses[i].pipelines.pairs: - if renderer.scenedata[scene].usesMaterialType(materialType): + if scene.usesMaterialType(materialType): debug &"Start pipeline for {materialType}" commandBuffer.vkCmdBindPipeline(renderer.renderPass.subpasses[i].pipelineBindPoint, pipeline.vk) commandBuffer.vkCmdBindDescriptorSets(renderer.renderPass.subpasses[i].pipelineBindPoint, pipeline.layout, 0, 1, addr(renderer.scenedata[scene].descriptorSets[pipeline.vk][renderer.swapchain.currentInFlight].vk), 0, nil) - for drawable in renderer.scenedata[scene].drawables.values: - if drawable.mesh.material != nil and drawable.mesh.material.materialType == materialType: + for (drawable, meshIndex) in renderer.scenedata[scene].drawables: + if scene.meshes[meshIndex].material != nil and scene.meshes[meshIndex].material.materialType == materialType: drawable.draw(commandBuffer, vertexBuffers=renderer.scenedata[scene].vertexBuffers, indexBuffer=renderer.scenedata[scene].indexBuffer) if i < renderer.renderPass.subpasses.len - 1: diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/resources.nim --- a/src/semicongine/resources.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/resources.nim Fri Aug 25 00:29:51 2023 +0700 @@ -9,8 +9,7 @@ import ./resources/audio import ./resources/mesh import ./resources/font - -from ./scene import Entity, Scene +import ./mesh export image export audio @@ -134,15 +133,10 @@ let defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=+[{]};:,<.>/? ".toRunes() loadResource_intern(path).readTrueType(name, defaultCharset, color, resolution) -proc loadMesh*(path: string): Entity = - loadResource_intern(path).readglTF()[0].root +proc loadFirstMesh*(path: string): Mesh = + loadResource_intern(path).readglTF()[0].children[0].mesh -proc loadScene*(path: string, name=""): Scene = - result = loadResource_intern(path).readglTF()[0] - if name != "": - result.name = name - -proc loadScenes*(path: string): seq[Scene] = +proc loadMeshes*(path: string): seq[MeshTree] = loadResource_intern(path).readglTF() proc modList*(): seq[string] = diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/resources/font.nim --- a/src/semicongine/resources/font.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/resources/font.nim Fri Aug 25 00:29:51 2023 +0700 @@ -33,7 +33,7 @@ result.resolution = resolution result.fontscale = float32(stbtt_ScaleForPixelHeight(addr fontinfo, cfloat(resolution))) var - offsetX: uint32 + offsetX = 0 bitmaps: Table[Rune, (cstring, cint, cint)] topOffsets: Table[Rune, int] for codePoint in codePoints: @@ -49,13 +49,13 @@ ) bitmaps[codePoint] = (data, width, height) result.maxHeight = max(result.maxHeight, int(height)) - offsetX += uint32(width + 1) + offsetX += width + 1 topOffsets[codePoint] = offY result.name = name result.fontAtlas = Texture( name: name & "_texture", - image: newImage(offsetX, uint32(result.maxHeight + 1)), + image: newImage(offsetX, result.maxHeight + 1), sampler: FONTSAMPLER_SOFT ) @@ -63,8 +63,8 @@ for codePoint in codePoints: let bitmap = bitmaps[codePoint][0] - width = uint32(bitmaps[codePoint][1]) - height = uint32(bitmaps[codePoint][2]) + width = bitmaps[codePoint][1] + height = bitmaps[codePoint][2] # bitmap data for y in 0 ..< height: diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/resources/image.nim --- a/src/semicongine/resources/image.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/resources/image.nim Fri Aug 25 00:29:51 2023 +0700 @@ -101,7 +101,7 @@ data[row_mult * dibHeader.width + col]= pixel stream.setPosition(stream.getPosition() + padding) - result = newImage(width=uint32(dibHeader.width), height=uint32(abs(dibHeader.height)), imagedata=data) + result = newImage(width=dibHeader.width, height=abs(dibHeader.height), imagedata=data) {.compile: currentSourcePath.parentDir() & "/lodepng.c" .} @@ -122,4 +122,4 @@ free(data) - result = newImage(width=w, height=h, imagedata=imagedata) + result = newImage(width=int(w), height=int(h), imagedata=imagedata) diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/resources/mesh.nim --- a/src/semicongine/resources/mesh.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/resources/mesh.nim Fri Aug 25 00:29:51 2023 +0700 @@ -6,7 +6,6 @@ import std/strformat import std/streams -import ../scene import ../mesh import ../core @@ -20,6 +19,9 @@ glTFData = object structuredContent: JsonNode binaryBufferData: seq[uint8] + MeshTree* = ref object + mesh*: Mesh + children*: seq[MeshTree] const JSON_CHUNK = 0x4E4F534A @@ -95,7 +97,7 @@ proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): DataList = result = newDataList(thetype=accessor.getGPUType()) - result.initData(uint32(accessor["count"].getInt())) + result.initData(accessor["count"].getInt()) let bufferView = root["bufferViews"][accessor["bufferView"].getInt()] assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" @@ -113,7 +115,7 @@ # we don't support stride, have to convert stuff here... does this even work? for i in 0 ..< int(result.len): copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView["byteStride"].getInt()], int(result.thetype.size)) - dstPointer = cast[pointer](cast[uint64](dstPointer) + result.thetype.size) + dstPointer = cast[pointer](cast[int](dstPointer) + result.thetype.size) else: copyMem(dstPointer, addr mainBuffer[bufferOffset], length) @@ -212,7 +214,7 @@ if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4: raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode") - var vertexCount = 0'u32 + var vertexCount = 0 for attribute, accessor in primitiveNode["attributes"].pairs: let data = root.getAccessorData(root["accessors"][accessor.getInt()], mainBuffer) mesh.appendAttributeData(attribute.toLowerAscii, data) @@ -221,7 +223,7 @@ var materialId = 0'u16 if primitiveNode.hasKey("material"): materialId = uint16(primitiveNode["material"].getInt()) - mesh.appendAttributeData("materialIndex", newSeqWith[uint8](int(vertexCount), materialId)) + mesh.appendAttributeData("materialIndex", newSeqWith[uint8](vertexCount, materialId)) let material = loadMaterial(root, root["materials"][int(materialId)], mainBuffer, materialId) # if mesh.material != nil and mesh.material[] != material[]: # raise newException(Exception, &"Only one material per mesh supported at the moment") @@ -231,18 +233,18 @@ assert mesh.indexType != None let data = root.getAccessorData(root["accessors"][primitiveNode["indices"].getInt()], mainBuffer) let baseIndex = mesh.indicesCount - var tri: seq[uint32] + var tri: seq[int] case data.thetype of UInt16: for entry in getValues[uint16](data)[]: - tri.add uint32(entry) + baseIndex + tri.add int(entry) + baseIndex if tri.len == 3: # FYI gltf uses counter-clockwise indexing mesh.appendIndicesData(tri[0], tri[2], tri[1]) tri.setLen(0) of UInt32: for entry in getValues[uint32](data)[]: - tri.add uint32(entry) + tri.add int(entry) if tri.len == 3: # FYI gltf uses counter-clockwise indexing mesh.appendIndicesData(tri[0], tri[2], tri[1]) @@ -265,7 +267,7 @@ else: indexType = Big - result = Mesh(instanceCount: 1, instanceTransforms: newSeqWith(1, Unit4F32), indexType: indexType) + result = Mesh(instanceTransforms: @[Unit4F32], indexType: indexType) # check we have the same attributes for all primitives let attributes = meshNode["primitives"][0]["attributes"].keys.toSeq @@ -274,37 +276,34 @@ # prepare mesh attributes for attribute, accessor in meshNode["primitives"][0]["attributes"].pairs: - result.setMeshData(attribute.toLowerAscii, newDataList(thetype=root["accessors"][accessor.getInt()].getGPUType())) - result.setMeshData("materialIndex", newDataList(theType=UInt16)) + result.initVertexAttribute(attribute.toLowerAscii, root["accessors"][accessor.getInt()].getGPUType()) + result.initInstanceAttribute("materialIndex", 0'u16) # add all mesh data for primitive in meshNode["primitives"]: result.addPrimitive(root, primitive, mainBuffer) - setInstanceData(result, "transform", newSeqWith(int(result.instanceCount), Unit4F32)) - -proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8]): Entity = - var name = "" - if node.hasKey("name"): - name = node["name"].getStr() - result = newEntity(name) +proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8]): MeshTree = + # mesh + if node.hasKey("mesh"): + result.mesh = loadMesh(root, root["meshes"][node["mesh"].getInt()], mainBuffer) # transformation if node.hasKey("matrix"): var mat: Mat4 for i in 0 ..< node["matrix"].len: mat[i] = node["matrix"][i].getFloat() - result.transform = mat + result.mesh.transform = mat else: var (t, r, s) = (Unit4F32, Unit4F32, Unit4F32) if node.hasKey("translation"): - t = translate3d( + t = translate( float32(node["translation"][0].getFloat()), float32(node["translation"][1].getFloat()), float32(node["translation"][2].getFloat()) ) if node.hasKey("rotation"): - t = rotate3d( + t = rotate( float32(node["rotation"][3].getFloat()), newVec3f( float32(node["rotation"][0].getFloat()), @@ -313,32 +312,25 @@ ) ) if node.hasKey("scale"): - t = scale3d( + t = scale( float32(node["scale"][0].getFloat()), float32(node["scale"][1].getFloat()), float32(node["scale"][2].getFloat()) ) - result.transform = t * r * s + result.mesh.transform = t * r * s # children if node.hasKey("children"): for childNode in node["children"]: - result.add loadNode(root, root["nodes"][childNode.getInt()], mainBuffer) - - # mesh - if node.hasKey("mesh"): - result["mesh"] = loadMesh(root, root["meshes"][node["mesh"].getInt()], mainBuffer) + result.children.add loadNode(root, root["nodes"][childNode.getInt()], mainBuffer) -proc loadScene(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8]): Scene = - var rootEntity = newEntity("") +proc loadMeshTree(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8]): MeshTree = + result = MeshTree() for nodeId in scenenode["nodes"]: - var node = loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer) - rootEntity.add node - - newScene(scenenode["name"].getStr(), rootEntity) + result.children.add loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer) -proc readglTF*(stream: Stream): seq[Scene] = +proc readglTF*(stream: Stream): seq[MeshTree] = var header: glTFHeader data: glTFData @@ -368,4 +360,4 @@ debug "Loading mesh: ", data.structuredContent.pretty for scenedata in data.structuredContent["scenes"]: - result.add data.structuredContent.loadScene(scenedata, data.binaryBufferData) + result.add data.structuredContent.loadMeshTree(scenedata, data.binaryBufferData) diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/scene.nim --- a/src/semicongine/scene.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/scene.nim Fri Aug 25 00:29:51 2023 +0700 @@ -6,76 +6,15 @@ import ./core import ./animation +import ./mesh type Scene* = object name*: string - root*: Entity shaderGlobals*: Table[string, DataList] transformAttribute*: string = "transform" materialIndexAttribute*: string = "materialIndex" - - Component* = ref object of RootObj - entity*: Entity - - Entity* = ref object of RootObj - name*: string - internal_transform: Mat4 # todo: cache transform + only update VBO when transform changed - parent: Entity - children: seq[Entity] - components: Table[string, Component] - - EntityAnimation* = ref object of Component - player: AnimationPlayer[Mat4] - -func newEntityAnimation*(animation: Animation[Mat4]): EntityAnimation = - result = EntityAnimation(player: newAnimator(animation)) - result.player.currentValue = Unit4 - -func setAnimation*(entityAnimation: EntityAnimation, animation: Animation[Mat4]) = - entityAnimation.player.animation = animation - entityAnimation.player.resetPlayer() - -func start*(animation: EntityAnimation) = - animation.player.start() - -func stop*(animation: EntityAnimation) = - animation.player.stop() - -func reset*(animation: EntityAnimation) = - animation.player.stop() - animation.player.resetPlayer() - -func playing*(animation: EntityAnimation): bool = - animation.player.playing - -func update*(animation: EntityAnimation, dt: float32) = - animation.player.advance(dt) - -func parent(entity: Entity): Entity = - entity.parent - -# TODO: this is wrong: transfrom setter + getter are not "symetric" -func transform*(entity: Entity): Mat4 = - result = entity.internal_transform - for component in entity.components.mvalues: - if component of EntityAnimation and EntityAnimation(component).player.playing: - result = result * EntityAnimation(component).player.currentValue - -func `transform=`*(entity: Entity, value: Mat4) = - entity.internal_transform = value - -# TODO: position-setter -func position*(entity: Entity): Vec3f = - return entity.transform.col(3) - -func originalTransform*(entity: Entity): Mat4 = - entity.internal_transform - -func getModelTransform*(entity: Entity): Mat4 = - result = entity.transform - if not entity.parent.isNil: - result = entity.transform * entity.parent.getModelTransform() + meshes*: seq[Mesh] func addShaderGlobal*[T](scene: var Scene, name: string, data: T) = scene.shaderGlobals[name] = newDataList(thetype=getDataType[T]()) @@ -100,137 +39,8 @@ func appendShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) = appendValues[T](scene.shaderGlobals[name], value) -func newScene*(name: string, root: Entity, transformAttribute="transform", materialIndexAttribute="materialIndex"): Scene = - Scene(name: name, root: root, transformAttribute: transformAttribute, materialIndexAttribute: materialIndexAttribute) - 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" -method `$`*(animation: EntityAnimation): string = - &"Entity animation: {animation.player.animation}" - -proc add*(entity: Entity, child: Entity) = - child.parent = entity - entity.children.add child -proc `[]=`*[T](entity: Entity, index: int, child: var T) = - child.parent = entity - entity.children[index] = child -proc `[]`*(entity: Entity, index: int): Entity = - entity.children[index] - -proc `[]=`*[T](entity: Entity, name: string, component: T) = - component.entity = entity - entity.components[name] = component -proc `[]`*[T](entity: Entity, name: string, component: T): T = - T(entity.components[name]) - -func newEntity*(name: string, components: openArray[(string, Component)] = [], children: varargs[Entity]): Entity = - result = new Entity - for child in children: - result.add child - result.name = name - for (name, comp) in components: - result[name] = comp - if result.name == "": - result.name = &"Entity[{$(cast[uint](result))}]" - result.internal_transform = Unit4 - -iterator allEntitiesOfType*[T: Entity](root: Entity): T = - var queue = @[root] - while queue.len > 0: - var entity = queue.pop - if entity of T: - yield T(entity) - for i in countdown(entity.children.len - 1, 0): - queue.add entity.children[i] - -iterator allComponentsOfType*[T: Component](root: Entity): var T = - var queue = @[root] - while queue.len > 0: - let entity = queue.pop - for component in entity.components.mvalues: - 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 `[]`*(scene: Scene, name: string): Entity = - return scene.root.firstWithName(name) - -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 - -proc prettyRecursive*(entity: Entity): seq[string] = - var compList: seq[string] - for (name, comp) in entity.components.pairs: - compList.add name & ": " & $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") diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/text.nim --- a/src/semicongine/text.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/text.nim Fri Aug 25 00:29:51 2023 +0700 @@ -1,8 +1,6 @@ -import std/sequtils import std/tables import std/unicode -import ./scene import ./mesh import ./core/vector import ./core/matrix @@ -13,8 +11,8 @@ Left Center Right - Textbox* = ref object of Entity - maxLen*: uint32 + Textbox* = object + maxLen*: int text: seq[Rune] dirty: bool alignment*: TextAlignment @@ -25,9 +23,9 @@ # pre-calculate text-width var width = 0'f32 - for i in 0 ..< min(uint32(textbox.text.len), textbox.maxLen): + for i in 0 ..< min(textbox.text.len, textbox.maxLen): width += textbox.font.glyphs[textbox.text[i]].advance - if i < uint32(textbox.text.len - 1): + if i < textbox.text.len - 1: width += textbox.font.kerning[(textbox.text[i], textbox.text[i + 1])] let centerX = width / 2 @@ -36,7 +34,7 @@ var offsetX = 0'f32 for i in 0 ..< textbox.maxLen: let vertexOffset = i * 4 - if i < uint32(textbox.text.len): + if i < textbox.text.len: let glyph = textbox.font.glyphs[textbox.text[i]] left = offsetX + glyph.leftOffset @@ -55,7 +53,7 @@ textbox.mesh.updateAttributeData("uv", vertexOffset + 3, glyph.uvs[3]) offsetX += glyph.advance - if i < uint32(textbox.text.len - 1): + if i < textbox.text.len - 1: offsetX += textbox.font.kerning[(textbox.text[i], textbox.text[i + 1])] else: textbox.mesh.updateAttributeData("position", vertexOffset + 0, newVec3f()) @@ -69,27 +67,23 @@ proc `text=`*(textbox: var Textbox, text: seq[Rune]) = textbox.text = text - textbox.name = $text textbox.updateMesh() -proc newTextbox*(maxLen: uint32, font: Font, text=toRunes("")): Textbox = +proc newTextbox*(maxLen: int, font: Font, text=toRunes("")): Textbox = var positions = newSeq[Vec3f](int(maxLen * 4)) - indices: seq[array[3, uint32]] + indices: seq[array[3, uint16]] uvs = newSeq[Vec2f](int(maxLen * 4)) for i in 0 ..< maxLen: let offset = i * 4 - indices.add [[offset + 0, offset + 1, offset + 2], [offset + 2, offset + 3, offset + 0]] + indices.add [ + [uint16(offset + 0), uint16(offset + 1), uint16(offset + 2)], + [uint16(offset + 2), uint16(offset + 3), uint16(offset + 0)], + ] result = Textbox(maxLen: maxLen, text: text, font: font, dirty: true) result.mesh = newMesh(positions = positions, indices = indices, uvs = uvs) - result.mesh.setInstanceTransforms(@[Unit4F32]) - result.name = $text - Entity(result).transform = Unit4F32 # wrap the text mesh in a new entity to preserve the font-scaling - var box = newEntity("box", {"mesh": Component(result.mesh)}) - # box.transform = scale3d(font.fontscale * 0.002, font.fontscale * 0.002) - box.transform = scale3d(1 / font.resolution, 1 / font.resolution) - result.add box + result.mesh.transform = scale(1 / font.resolution, 1 / font.resolution) result.updateMesh() diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/buffer.nim --- a/src/semicongine/vulkan/buffer.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/buffer.nim Fri Aug 25 00:29:51 2023 +0700 @@ -14,7 +14,7 @@ Buffer* = object device*: Device vk*: VkBuffer - size*: uint64 + size*: int usage*: seq[VkBufferUsageFlagBits] case memoryAllocated*: bool of false: discard @@ -67,7 +67,7 @@ # (shardingMode = VK_SHARING_MODE_CONCURRENT not supported) proc createBuffer*( device: Device, - size: uint64, + size: int, usage: openArray[VkBufferUsageFlagBits], requireMappable: bool, preferVRAM: bool, @@ -84,7 +84,7 @@ var createInfo = VkBufferCreateInfo( sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, flags: VkBufferCreateFlags(0), - size: size, + size: uint64(size), usage: toBits(result.usage), sharingMode: VK_SHARING_MODE_EXCLUSIVE, ) @@ -98,7 +98,7 @@ result.allocateMemory(requireMappable=requireMappable, preferVRAM=preferVRAM, preferAutoFlush=preferAutoFlush) -proc copy*(src, dst: Buffer, dstOffset=0'u64) = +proc copy*(src, dst: Buffer, dstOffset=0) = assert src.device.vk.valid assert dst.device.vk.valid assert src.device == dst.device @@ -106,7 +106,7 @@ assert VK_BUFFER_USAGE_TRANSFER_SRC_BIT in src.usage assert VK_BUFFER_USAGE_TRANSFER_DST_BIT in dst.usage - var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: dstOffset) + var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: VkDeviceSize(dstOffset)) withSingleUseCommandBuffer(src.device, true, commandBuffer): commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion)) @@ -126,10 +126,10 @@ ) buffer.vk.reset -proc setData*(dst: Buffer, src: pointer, size: uint64, bufferOffset=0'u64) = +proc setData*(dst: Buffer, src: pointer, size: int, bufferOffset=0) = assert bufferOffset + size <= dst.size if dst.memory.canMap: - copyMem(cast[pointer](cast[uint64](dst.memory.data) + bufferOffset), src, size) + copyMem(cast[pointer](cast[int](dst.memory.data) + bufferOffset), src, size) if dst.memory.needsFlushing: dst.memory.flush() else: # use staging buffer, slower but required if memory is not host visible diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/descriptor.nim --- a/src/semicongine/vulkan/descriptor.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/descriptor.nim Fri Aug 25 00:29:51 2023 +0700 @@ -11,13 +11,13 @@ Uniform, ImageSampler Descriptor* = object # "fields" of a DescriptorSetLayout name*: string - count*: uint32 + count*: int stages*: seq[VkShaderStageFlagBits] case thetype*: DescriptorType of Uniform: buffer*: Buffer - offset*: uint64 - size*: uint64 + offset*: int + size*: int of ImageSampler: imageviews*: seq[ImageView] samplers*: seq[VulkanSampler] @@ -31,8 +31,8 @@ DescriptorPool* = object # required for allocation of DescriptorSet device: Device vk*: VkDescriptorPool - maxSets*: uint32 # maximum number of allocatable descriptor sets - counts*: seq[(VkDescriptorType, uint32)] # maximum number for each descriptor type to allocate + maxSets*: int # maximum number of allocatable descriptor sets + counts*: seq[(VkDescriptorType, int)] # maximum number for each descriptor type to allocate const DESCRIPTOR_TYPE_MAP = { Uniform: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, @@ -53,7 +53,7 @@ layoutbindings.add VkDescriptorSetLayoutBinding( binding: uint32(i), descriptorType: descriptor.vkType, - descriptorCount: descriptor.count, + descriptorCount: uint32(descriptor.count), stageFlags: toBits descriptor.stages, pImmutableSamplers: nil, ) @@ -71,7 +71,7 @@ descriptorSetLayout.vk.reset -proc createDescriptorSetPool*(device: Device, counts: seq[(VkDescriptorType, uint32)], maxSets = 1000'u32): DescriptorPool = +proc createDescriptorSetPool*(device: Device, counts: seq[(VkDescriptorType, int)], maxSets = 1000): DescriptorPool = assert device.vk.valid result.device = device @@ -80,12 +80,12 @@ var poolSizes: seq[VkDescriptorPoolSize] for (thetype, count) in result.counts: - poolSizes.add VkDescriptorPoolSize(thetype: thetype, descriptorCount: count) + poolSizes.add VkDescriptorPoolSize(thetype: thetype, descriptorCount: uint32(count)) var poolInfo = VkDescriptorPoolCreateInfo( sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, poolSizeCount: uint32(poolSizes.len), pPoolSizes: poolSizes.toCPointer, - maxSets: result.maxSets, + maxSets: uint32(result.maxSets), ) checkVkResult vkCreateDescriptorPool(result.device.vk, addr(poolInfo), nil, addr(result.vk)) @@ -139,8 +139,8 @@ assert descriptor.buffer.vk.valid bufferInfos.add VkDescriptorBufferInfo( buffer: descriptor.buffer.vk, - offset: descriptor.offset, - range: descriptor.size, + offset: uint64(descriptor.offset), + range: uint64(descriptor.size), ) descriptorSetWrites.add VkWriteDescriptorSet( sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -148,7 +148,7 @@ dstBinding: i, dstArrayElement: 0, descriptorType: descriptor.vkType, - descriptorCount: descriptor.count, + descriptorCount: uint32(descriptor.count), pBufferInfo: addr bufferInfos[^1], ) elif descriptor.thetype == ImageSampler: @@ -168,7 +168,7 @@ dstBinding: i, dstArrayElement: 0, descriptorType: descriptor.vkType, - descriptorCount: descriptor.count, + descriptorCount: uint32(descriptor.count), pImageInfo: imgInfos[^1].toCPointer, ) inc i diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/drawable.nim --- a/src/semicongine/vulkan/drawable.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/drawable.nim Fri Aug 25 00:29:51 2023 +0700 @@ -3,20 +3,17 @@ import std/logging import ../core -import ../mesh -import ../scene import ./buffer type Drawable* = object - mesh*: Mesh - elementCount*: uint32 # number of vertices or indices - bufferOffsets*: seq[(string, MemoryPerformanceHint, uint64)] # list of buffers and list of offset for each attribute in that buffer - instanceCount*: uint32 # number of instance + elementCount*: int # number of vertices or indices + bufferOffsets*: seq[(string, MemoryPerformanceHint, int)] # list of buffers and list of offset for each attribute in that buffer + instanceCount*: int # number of instance case indexed*: bool of true: indexType*: VkIndexType - indexBufferOffset*: uint64 + indexBufferOffset*: int of false: discard @@ -27,8 +24,6 @@ &"Drawable(elementCount: {drawable.elementCount}, instanceCount: {drawable.instanceCount}, bufferOffsets: {drawable.bufferOffsets})" proc draw*(drawable: Drawable, commandBuffer: VkCommandBuffer, vertexBuffers: Table[MemoryPerformanceHint, Buffer], indexBuffer: Buffer) = - if drawable.mesh.entity.transform == Mat4(): - return debug "Draw ", drawable var buffers: seq[VkBuffer] @@ -47,16 +42,16 @@ if drawable.indexed: commandBuffer.vkCmdBindIndexBuffer(indexBuffer.vk, VkDeviceSize(drawable.indexBufferOffset), drawable.indexType) commandBuffer.vkCmdDrawIndexed( - indexCount=drawable.elementCount, - instanceCount=drawable.instanceCount, + indexCount=uint32(drawable.elementCount), + instanceCount=uint32(drawable.instanceCount), firstIndex=0, vertexOffset=0, firstInstance=0 ) else: commandBuffer.vkCmdDraw( - vertexCount=drawable.elementCount, - instanceCount=drawable.instanceCount, + vertexCount=uint32(drawable.elementCount), + instanceCount=uint32(drawable.instanceCount), firstVertex=0, firstInstance=0 ) diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/image.nim --- a/src/semicongine/vulkan/image.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/image.nim Fri Aug 25 00:29:51 2023 +0700 @@ -9,12 +9,12 @@ import ./commandbuffer type - PixelDepth = 1'u32 .. 4'u32 + PixelDepth = 1 .. 4 VulkanImage* = object device*: Device vk*: VkImage - width*: uint32 # pixel - height*: uint32 # pixel + width*: int # pixel + height*: int # pixel depth*: PixelDepth format*: VkFormat usage*: seq[VkImageUsageFlagBits] @@ -137,7 +137,7 @@ layerCount: 1, ), imageOffset: VkOffset3D(x: 0, y: 0, z: 0), - imageExtent: VkExtent3D(width: dst.width, height: dst.height, depth: 1) + imageExtent: VkExtent3D(width: uint32(dst.width), height: uint32(dst.height), depth: 1) ) withSingleUseCommandBuffer(src.device, true, commandBuffer): commandBuffer.vkCmdCopyBufferToImage( @@ -149,7 +149,7 @@ ) # currently only usable for texture access from shader -proc createImage*(device: Device, width, height: uint32, depth: PixelDepth, data: pointer): VulkanImage = +proc createImage*(device: Device, width, height: int, depth: PixelDepth, data: pointer): VulkanImage = assert device.vk.valid assert width > 0 assert height > 0 @@ -167,7 +167,7 @@ var imageInfo = VkImageCreateInfo( sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, imageType: VK_IMAGE_TYPE_2D, - extent: VkExtent3D(width: width, height: height, depth: 1), + extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1), mipLevels: 1, arrayLayers: 1, format: result.format, diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/pipeline.nim --- a/src/semicongine/vulkan/pipeline.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Fri Aug 25 00:29:51 2023 +0700 @@ -34,11 +34,11 @@ result = descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, inFlightFrames) for i in 0 ..< inFlightFrames: - var offset = 0'u64 + var offset = 0 # first descriptor is always uniform for globals, match should be better somehow for descriptor in result[i].layout.descriptors.mitems: if descriptor.thetype == Uniform and buffers.len > 0: - let size = VkDeviceSize(descriptor.size) + let size = descriptor.size descriptor.buffer = buffers[i] descriptor.offset = offset descriptor.size = size @@ -76,7 +76,7 @@ descriptors.add Descriptor( name: sampler.name, thetype: ImageSampler, - count: (if sampler.arrayCount == 0: 1'u32 else: sampler.arrayCount), + count: (if sampler.arrayCount == 0: 1 else: sampler.arrayCount), stages: @[VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT], ) result.descriptorSetLayout = device.createDescriptorSetLayout(descriptors) diff -r 956e9c7e36da -r 670ca93a358b src/semicongine/vulkan/shader.nim --- a/src/semicongine/vulkan/shader.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/src/semicongine/vulkan/shader.nim Fri Aug 25 00:29:51 2023 +0700 @@ -180,7 +180,7 @@ for attribute in shaderConfiguration.inputs: bindings.add VkVertexInputBindingDescription( binding: binding, - stride: attribute.size, + stride: uint32(attribute.size), inputRate: if attribute.perInstance: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, ) # allows to submit larger data structures like Mat44, for most other types will be 1 @@ -189,9 +189,9 @@ binding: binding, location: location, format: attribute.thetype.getVkFormat, - offset: i * attribute.size(perDescriptor=true), + offset: uint32(i * attribute.size(perDescriptor=true)), ) - location += attribute.thetype.nLocationSlots + location += uint32(attribute.thetype.nLocationSlots) inc binding return VkPipelineVertexInputStateCreateInfo( diff -r 956e9c7e36da -r 670ca93a358b tests/test_vulkan_wrapper.nim --- a/tests/test_vulkan_wrapper.nim Mon Aug 21 00:17:16 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Fri Aug 25 00:29:51 2023 +0700 @@ -41,114 +41,120 @@ name: "mat3", materialType: "my_special_material", constants: { - "color": toGPUValue(newVec4f(0.5, 0.5, 0)) + "color": toGPUValue(newVec4f(0, 1, 0, 1)) }.toTable ) -proc scene_different_mesh_types(): Entity = - result = newEntity("root", [], - newEntity("triangle1", {"mesh": Component(newMesh( +proc scene_different_mesh_types(): seq[Mesh] = + @[ + newMesh( positions=[newVec3f(0.0, -0.5), newVec3f(0.5, 0.5), newVec3f(-0.5, 0.5)], colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], material=mat, - ))}), - newEntity("triangle1b", {"mesh": Component(newMesh( + transform=translate(-0.7, -0.5), + ), + newMesh( positions=[newVec3f(0.0, -0.4), newVec3f(0.4, 0.4), newVec3f(-0.4, 0.5)], colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], material=mat, - ))}), - newEntity("triangle2a", {"mesh": Component(newMesh( + transform=translate(0, -0.5), + ), + newMesh( positions=[newVec3f(0.0, 0.5), newVec3f(0.5, -0.5), newVec3f(-0.5, -0.5)], colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], indices=[[0'u16, 2'u16, 1'u16]], material=mat2, - ))}), - newEntity("triangle2b", {"mesh": Component(newMesh( + transform=translate(0.7, -0.5), + ), + newMesh( positions=[newVec3f(0.0, 0.4), newVec3f(0.4, -0.4), newVec3f(-0.4, -0.4)], colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], indices=[[0'u16, 2'u16, 1'u16]], material=mat2, - ))}), - newEntity("triangle3a", {"mesh": Component(newMesh( - positions=[newVec3f(0.4, 0.5), newVec3f(0.9, -0.3), newVec3f(0.0, -0.3)], - colors=[newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1)], - indices=[[0'u32, 2'u32, 1'u32]], - autoResize=false, - material=mat2, - ))}), - newEntity("triangle3b", {"mesh": Component(newMesh( + transform=translate(-0.7, 0.5), + ), + newMesh( positions=[newVec3f(0.4, 0.5), newVec3f(0.9, -0.3), newVec3f(0.0, -0.3)], colors=[newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1)], indices=[[0'u32, 2'u32, 1'u32]], autoResize=false, material=mat2, - ))}), - ) - for mesh in allComponentsOfType[Mesh](result): - mesh.setInstanceData("translate", @[newVec3f()]) - result[0]["mesh", Mesh()].updateInstanceData("translate", @[newVec3f(-0.6, -0.6)]) - result[1]["mesh", Mesh()].updateInstanceData("translate", @[newVec3f(-0.6, 0.6)]) - result[2]["mesh", Mesh()].updateInstanceData("translate", @[newVec3f(0.6, -0.6)]) - result[3]["mesh", Mesh()].updateInstanceData("translate", @[newVec3f(0.6, 0.6)]) + transform=translate(0, 0.5), + ), + newMesh( + positions=[newVec3f(0.4, 0.5), newVec3f(0.9, -0.3), newVec3f(0.0, -0.3)], + colors=[newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1)], + indices=[[0'u32, 2'u32, 1'u32]], + autoResize=false, + material=mat2, + transform=translate(0.7, 0.5), + ), + ] -proc scene_simple(): Entity = - var mymesh1 = newMesh( - positions=[newVec3f(0.0, -0.3), newVec3f(0.3, 0.3), newVec3f(-0.3, 0.3)], - colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], - material=mat, - ) - var mymesh2 = newMesh( - positions=[newVec3f(0.0, -0.5), newVec3f(0.5, 0.5), newVec3f(-0.5, 0.5)], - colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], - material=mat, - ) - var mymesh3 = newMesh( - positions=[newVec3f(0.0, -0.6), newVec3f(0.6, 0.6), newVec3f(-0.6, 0.6)], - colors=[newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1)], - indices=[[0'u32, 1'u32, 2'u32]], - autoResize=false, - material=mat, - ) - var mymesh4 = newMesh( - positions=[newVec3f(0.0, -0.8), newVec3f(0.8, 0.8), newVec3f(-0.8, 0.8)], - colors=[newVec4f(0.0, 0.0, 1.0, 1), newVec4f(0.0, 0.0, 1.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], - indices=[[0'u16, 1'u16, 2'u16]], - instanceCount=2, - material=mat, - ) - mymesh1.setInstanceData("translate", @[newVec3f( 0.4, 0.4)]) - mymesh2.setInstanceData("translate", @[newVec3f( 0.4, -0.4)]) - mymesh3.setInstanceData("translate", @[newVec3f(-0.4, -0.4)]) - mymesh4.setInstanceData("translate", @[newVec3f(-0.4, 0.4), newVec3f(0.0, 0.0)]) - result = newEntity("root", [], newEntity("triangle", {"mesh1": Component(mymesh4), "mesh2": Component(mymesh3), "mesh3": Component(mymesh2), "mesh4": Component(mymesh1)})) +proc scene_simple(): seq[Mesh] = + @[ + newMesh( + positions=[newVec3f(0.0, -0.3), newVec3f(0.3, 0.3), newVec3f(-0.3, 0.3)], + colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], + material=mat, + transform=translate(0.4, 0.4), + ), + newMesh( + positions=[newVec3f(0.0, -0.5), newVec3f(0.5, 0.5), newVec3f(-0.5, 0.5)], + colors=[newVec4f(1.0, 0.0, 0.0, 1), newVec4f(0.0, 1.0, 0.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], + material=mat, + transform=translate(0.4, -0.4), + ), + newMesh( + positions=[newVec3f(0.0, -0.6), newVec3f(0.6, 0.6), newVec3f(-0.6, 0.6)], + colors=[newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1), newVec4f(1.0, 1.0, 0.0, 1)], + indices=[[0'u32, 1'u32, 2'u32]], + autoResize=false, + material=mat, + transform=translate(-0.4, 0.4), + ), + newMesh( + positions=[newVec3f(0.0, -0.8), newVec3f(0.8, 0.8), newVec3f(-0.8, 0.8)], + colors=[newVec4f(0.0, 0.0, 1.0, 1), newVec4f(0.0, 0.0, 1.0, 1), newVec4f(0.0, 0.0, 1.0, 1)], + indices=[[0'u16, 1'u16, 2'u16]], + instanceTransforms=[Unit4F32, Unit4F32], + material=mat, + transform=translate(-0.4, -0.4), + ) + ] -proc scene_primitives(): Entity = +proc scene_primitives(): seq[Mesh] = var r = rect(color="ff0000") var t = tri(color="0000ff") var c = circle(color="00ff00") r.material = mat t.material = mat c.material = mat - - r.setInstanceData("translate", @[newVec3f(0.5, -0.3)]) - t.setInstanceData("translate", @[newVec3f(0.3, 0.3)]) - c.setInstanceData("translate", @[newVec3f(-0.3, 0.1)]) - result = newEntity("root", {"mesh1": Component(t), "mesh2": Component(r), "mesh3": Component(c)}) + r.transform = translate(newVec3f(0.5, -0.3)) + t.transform = translate(newVec3f(0.3, 0.3)) + c.transform = translate(newVec3f(-0.3, 0.1)) + result = @[r, c, t] -proc scene_flag(): Entity = - var r = rect(color="ffffff") - r.material = mat - result = newEntity("root", {"mesh": Component(r)}) +proc scene_flag(): seq[Mesh] = + @[ + newMesh( + positions=[newVec3f(-1.0, -1.0), newVec3f(1.0, -1.0), newVec3f(1.0, 1.0), newVec3f(-1.0, 1.0)], + colors=[newVec4f(-1, -1, 1, 1), newVec4f(1, -1, 1, 1), newVec4f(1, 1, 1, 1), newVec4f(-1, 1, 1, 1)], + indices=[[0'u16, 1'u16, 2'u16], [2'u16, 3'u16, 0'u16]], + material=mat, + transform=scale(0.5, 0.5) + ) + ] -proc scene_multi_material(): Entity = +proc scene_multi_material(): seq[Mesh] = var r1 = rect(color="ffffff") r2 = rect(color="000000") r1.material = mat r2.material = mat3 - r1.setInstanceData("translate", @[newVec3f(-0.5)]) - r2.setInstanceData("translate", @[newVec3f(+0.5)]) - result = newEntity("root", {"mesh1": Component(r1), "mesh2": Component(r2)}) + r1.transform = translate(newVec3f(-0.5)) + r2.transform = translate(newVec3f(+0.5)) + result = @[r1, r2] proc main() = var engine = initEngine("Test") @@ -159,7 +165,7 @@ inputs=[ attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead), attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite), - attr[Vec3f]("translate", perInstance=true), + attr[Mat4]("transform", perInstance=true), attr[uint16]("materialIndex", perInstance=true), ], intermediates=[ @@ -171,19 +177,19 @@ samplers=[ attr[Sampler2DType]("my_little_texture", arrayCount=2) ], - vertexCode="""gl_Position = vec4(position + translate, 1.0); outcolor = color; materialIndexOut = materialIndex;""", + vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = color; materialIndexOut = materialIndex;""", fragmentCode="color = texture(my_little_texture[materialIndexOut], outcolor.xy) * 0.5 + outcolor * 0.5;", ) shaderConfiguration2 = createShaderConfiguration( inputs=[ attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead), - attr[Vec3f]("translate", perInstance=true), + attr[Mat4]("transform", perInstance=true), ], intermediates=[attr[Vec4f]("outcolor")], outputs=[attr[Vec4f]("color")], uniforms=[attr[Vec4f]("color")], - vertexCode="""gl_Position = vec4(position + translate, 1.0); outcolor = Uniforms.color;""", - fragmentCode="color = outcolor;", + vertexCode="""gl_Position = vec4(position, 1.0) * transform; outcolor = Uniforms.color;""", + fragmentCode="color = vec4(0, 0, 1, 1);", ) engine.initRenderer({ "my_material": shaderConfiguration1, @@ -192,22 +198,24 @@ # INIT SCENES var scenes = [ - newScene("simple", scene_simple(), transformAttribute=""), - newScene("different mesh types", scene_different_mesh_types(), transformAttribute=""), - newScene("primitives", scene_primitives(), transformAttribute=""), - newScene("flag", scene_multi_material(), transformAttribute=""), + # Scene(name: "simple", meshes: scene_simple()), + # Scene(name: "different mesh types", meshes: scene_different_mesh_types()), + # Scene(name: "primitives", meshes: scene_primitives()), + # Scene(name: "flag", meshes: scene_flag()), + # Scene(name: "multimaterial", meshes: scene_multi_material(), materialIndexAttribute: ""), + Scene(name: "multimaterial", meshes: scene_multi_material()), ] - scenes[3].addShaderGlobal("color", newVec4f(1, 0, 1)) + scenes[0].addShaderGlobal("color", newVec4f(1, 0, 0, 1)) for scene in scenes.mitems: scene.addShaderGlobal("time", 0.0'f32) engine.addScene(scene) - # MAINLOOP echo "Setup successfull, start rendering" for i in 0 ..< 3: for scene in scenes.mitems: + echo "rendering scene ", scene.name for i in 0 ..< 1000: if engine.updateInputs() != Running or engine.keyIsDown(Escape): engine.destroy()