# HG changeset patch # User Sam # Date 1684085640 -25200 # Node ID 2ca938595aea8848e2aa3c50652895ee12e5842b # Parent 9767dec81193e78eb97db347e66432ecd849623d add: basic loading of glTF files (*.glb), no materials yet diff -r 9767dec81193 -r 2ca938595aea src/semicongine/core/gpu_data.nim --- a/src/semicongine/core/gpu_data.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/core/gpu_data.nim Mon May 15 00:34:00 2023 +0700 @@ -448,6 +448,8 @@ of Sampler2D: result[0] = nil func getRawData*(value: var DataList): (pointer, uint32) = + if value.len == 0: + return (nil, 0'u32) result[1] = value.thetype.size * value.len case value.thetype of Float32: result[0] = addr value.float32[0] @@ -644,6 +646,106 @@ elif T is TMat4[float64]: value.mat4f64 = data else: {. error: "Virtual datatype has no values" .} +func appendValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) = + 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 + elif T is int16: value.int16.add data + elif T is int32: value.int32.add data + elif T is int64: value.int64.add data + elif T is uint8: value.uint8.add data + elif T is uint16: value.uint16.add data + elif T is uint32: value.uint32.add data + elif T is uint64: value.uint64.add data + elif T is int and sizeof(int) == sizeof(int32): value.int32.add data + elif T is int and sizeof(int) == sizeof(int64): value.int64.add data + elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32.add data + elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64.add data + elif T is float and sizeof(float) == sizeof(float32): value.float32.add data + elif T is float and sizeof(float) == sizeof(float64): value.float64.add data + elif T is TVec2[int32]: value.vec2i32.add data + elif T is TVec2[int64]: value.vec2i64.add data + elif T is TVec3[int32]: value.vec3i32.add data + elif T is TVec3[int64]: value.vec3i64.add data + elif T is TVec4[int32]: value.vec4i32.add data + elif T is TVec4[int64]: value.vec4i64.add data + elif T is TVec2[uint32]: value.vec2u32.add data + elif T is TVec2[uint64]: value.vec2u64.add data + elif T is TVec3[uint32]: value.vec3u32.add data + elif T is TVec3[uint64]: value.vec3u64.add data + elif T is TVec4[uint32]: value.vec4u32.add data + elif T is TVec4[uint64]: value.vec4u64.add data + elif T is TVec2[float32]: value.vec2f32.add data + elif T is TVec2[float64]: value.vec2f64.add data + elif T is TVec3[float32]: value.vec3f32.add data + elif T is TVec3[float64]: value.vec3f64.add data + elif T is TVec4[float32]: value.vec4f32.add data + elif T is TVec4[float64]: value.vec4f64.add data + elif T is TMat2[float32]: value.mat2f32.add data + elif T is TMat2[float64]: value.mat2f64.add data + elif T is TMat23[float32]: value.mat23f32.add data + elif T is TMat23[float64]: value.mat23f64.add data + elif T is TMat32[float32]: value.mat32f32.add data + elif T is TMat32[float64]: value.mat32f64.add data + elif T is TMat3[float32]: value.mat3f32.add data + elif T is TMat3[float64]: value.mat3f64.add data + elif T is TMat34[float32]: value.mat34f32.add data + elif T is TMat34[float64]: value.mat34f64.add data + elif T is TMat43[float32]: value.mat43f32.add data + elif T is TMat43[float64]: value.mat43f64.add data + elif T is TMat4[float32]: value.mat4f32.add data + elif T is TMat4[float64]: value.mat4f64.add data + else: {. error: "Virtual datatype has no values" .} + +func appendValues*(value: var DataList, data: DataList) = + assert value.thetype == data.thetype + value.len += data.len + case value.thetype: + of Float32: value.float32.add data.float32 + of Float64: value.float64.add data.float64 + of Int8: value.int8.add data.int8 + of Int16: value.int16.add data.int16 + of Int32: value.int32.add data.int32 + of Int64: value.int64.add data.int64 + of UInt8: value.uint8.add data.uint8 + of UInt16: value.uint16.add data.uint16 + of UInt32: value.uint32.add data.uint32 + of UInt64: value.uint64.add data.uint64 + of Vec2I32: value.vec2i32.add data.vec2i32 + of Vec2I64: value.vec2i64.add data.vec2i64 + of Vec3I32: value.vec3i32.add data.vec3i32 + of Vec3I64: value.vec3i64.add data.vec3i64 + of Vec4I32: value.vec4i32.add data.vec4i32 + of Vec4I64: value.vec4i64.add data.vec4i64 + of Vec2U32: value.vec2u32.add data.vec2u32 + of Vec2U64: value.vec2u64.add data.vec2u64 + of Vec3U32: value.vec3u32.add data.vec3u32 + of Vec3U64: value.vec3u64.add data.vec3u64 + of Vec4U32: value.vec4u32.add data.vec4u32 + of Vec4U64: value.vec4u64.add data.vec4u64 + of Vec2F32: value.vec2f32.add data.vec2f32 + of Vec2F64: value.vec2f64.add data.vec2f64 + of Vec3F32: value.vec3f32.add data.vec3f32 + of Vec3F64: value.vec3f64.add data.vec3f64 + of Vec4F32: value.vec4f32.add data.vec4f32 + of Vec4F64: value.vec4f64.add data.vec4f64 + of Mat2F32: value.mat2f32.add data.mat2f32 + of Mat2F64: value.mat2f64.add data.mat2f64 + of Mat23F32: value.mat23f32.add data.mat23f32 + of Mat23F64: value.mat23f64.add data.mat23f64 + of Mat32F32: value.mat32f32.add data.mat32f32 + of Mat32F64: value.mat32f64.add data.mat32f64 + of Mat3F32: value.mat3f32.add data.mat3f32 + of Mat3F64: value.mat3f64.add data.mat3f64 + of Mat34F32: value.mat34f32.add data.mat34f32 + of Mat34F64: value.mat34f64.add data.mat34f64 + of Mat43F32: value.mat43f32.add data.mat43f32 + of Mat43F64: value.mat43f64.add data.mat43f64 + of Mat4F32: value.mat4f32.add data.mat4f32 + 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) = assert i < value.len when T is float32: value.float32[i] = data diff -r 9767dec81193 -r 2ca938595aea src/semicongine/core/vulkanapi.nim --- a/src/semicongine/core/vulkanapi.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/core/vulkanapi.nim Mon May 15 00:34:00 2023 +0700 @@ -4,7 +4,6 @@ import std/logging import std/typetraits import std/macros -import std/private/digitsutils type VkHandle* = distinct uint VkNonDispatchableHandle* = distinct uint @@ -12063,6 +12062,4 @@ vkEnumerateInstanceLayerProperties = cast[proc(pPropertyCount: ptr uint32, pProperties: ptr VkLayerProperties): VkResult {.stdcall.}](vkGetInstanceProcAddr(instance, "vkEnumerateInstanceLayerProperties")) vkCreateInstance = cast[proc(pCreateInfo: ptr VkInstanceCreateInfo, pAllocator: ptr VkAllocationCallbacks, pInstance: ptr VkInstance): VkResult {.stdcall.}](vkGetInstanceProcAddr(instance, "vkCreateInstance")) -converter VkBool2NimBool*(a: VkBool32): bool = a > 0 converter NimBool2VkBool*(a: bool): VkBool32 = VkBool32(a) -proc `$`*(x: uint32): string {.raises: [].} = addInt(result, x) diff -r 9767dec81193 -r 2ca938595aea src/semicongine/materials.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/materials.nim Mon May 15 00:34:00 2023 +0700 @@ -0,0 +1,23 @@ +import ./core + +type + # based on the default material + # from glTF + PBRMetalicRoughness* = object + name: string + + baseColor: Vec4f + baseColorTexture: Image + + metalic: float32 + roughness: float32 + metalicRoughnessTexture: Image + + normalScale: float32 + normalTexture: Image + + occlusionStrength: float32 + occlusionTexture: Image + + emissiveFactor: float32 + emissiveTexture: Image diff -r 9767dec81193 -r 2ca938595aea src/semicongine/mesh.nim --- a/src/semicongine/mesh.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/mesh.nim Mon May 15 00:34:00 2023 +0700 @@ -15,8 +15,6 @@ Small # up to 2^16 vertices Big # up to 2^32 vertices Mesh* = ref object of Component - vertexCount*: uint32 - indicesCount*: uint32 instanceCount*: uint32 data: Table[string, DataList] changedAttributes: seq[string] @@ -33,9 +31,30 @@ of Small: VK_INDEX_TYPE_UINT16 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) + +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) + method `$`*(mesh: Mesh): string = - &"Mesh ({mesh.vertexCount})" + &"Mesh, vertexCount: {mesh.vertexCount}, vertexData: {mesh.data.keys().toSeq()}, indexType: {mesh.indexType}" +func prettyData*(mesh: Mesh): string = + for attr, data in mesh.data.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 newMesh*( positions: openArray[Vec3f], @@ -49,8 +68,6 @@ assert uvs.len == 0 or uvs.len == positions.len result = new Mesh - result.vertexCount = uint32(positions.len) - result.indicesCount = uint32(indices.len * 3) result.instanceCount = instanceCount result.data["position"] = DataList(thetype: Vec3F32) setValues(result.data["position"], positions.toSeq) @@ -129,7 +146,6 @@ proc initData*(mesh: var Mesh, attribute: ShaderAttribute) = assert not (attribute.name in mesh.data) mesh.data[attribute.name] = DataList(thetype: attribute.thetype) - echo "Init ", attribute, " of ", mesh if attribute.perInstance: mesh.data[attribute.name].initData(mesh.instanceCount) else: @@ -140,11 +156,9 @@ mesh.data[attribute] = DataList(thetype: getDataType[T]()) setValues(mesh.data[attribute], data) -proc setInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = - assert uint32(data.len) == mesh.instanceCount +proc setMeshData*(mesh: var Mesh, attribute: string, data: DataList) = assert not (attribute in mesh.data) - mesh.data[attribute] = DataList(thetype: getDataType[T]()) - setValues(mesh.data[attribute], data) + mesh.data[attribute] = data proc updateMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = assert attribute in mesh.data @@ -156,12 +170,43 @@ mesh.changedAttributes.add attribute setValue(mesh.data[attribute], i, value) +proc appendMeshData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = + assert attribute in mesh.data + mesh.changedAttributes.add attribute + appendValues(mesh.data[attribute], data) + +# currently only used for loading from files, shouls +proc appendMeshData*(mesh: var Mesh, attribute: string, data: DataList) = + assert attribute in mesh.data + assert data.thetype == mesh.data[attribute].thetype + mesh.changedAttributes.add attribute + appendValues(mesh.data[attribute], data) + +proc setInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = + assert uint32(data.len) == mesh.instanceCount + assert not (attribute in mesh.data) + mesh.data[attribute] = DataList(thetype: getDataType[T]()) + setValues(mesh.data[attribute], data) + proc updateInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = assert uint32(data.len) == mesh.instanceCount assert attribute in mesh.data mesh.changedAttributes.add attribute setValues(mesh.data[attribute], data) +proc appendInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) = + assert uint32(data.len) == mesh.instanceCount + assert attribute in mesh.data + mesh.changedAttributes.add attribute + appendValues(mesh.data[attribute], data) + +proc appendIndicesData*(mesh: var Mesh, v1, v2, v3: uint32) = + 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]) + func hasDataChanged*(mesh: Mesh, attribute: string): bool = attribute in mesh.changedAttributes @@ -170,8 +215,6 @@ func rect*(width=1'f32, height=1'f32, color="ffffffff"): Mesh = result = new Mesh - result.vertexCount = 4 - result.indicesCount = 6 result.instanceCount = 1 result.data["position"] = DataList(thetype: Vec3F32) result.data["color"] = DataList(thetype: Vec4F32) @@ -191,7 +234,7 @@ func tri*(width=1'f32, height=1'f32, color="ffffffff"): Mesh = result = new Mesh - result.vertexCount = 3 + # result.vertexCount = 3 result.instanceCount = 1 result.data["position"] = DataList(thetype: Vec3F32) result.data["color"] = DataList(thetype: Vec4F32) @@ -207,7 +250,7 @@ func circle*(width=1'f32, height=1'f32, nSegments=12'u16, color="ffffffff"): Mesh = assert nSegments >= 3 result = new Mesh - result.vertexCount = nSegments + 2 + # result.vertexCount = nSegments + 2 result.instanceCount = 1 result.indexType = Small result.data["position"] = DataList(thetype: Vec3F32) @@ -225,6 +268,5 @@ col.add c result.smallIndices.add [0'u16, i + 1, i + 2] - result.indicesCount = uint32(result.smallIndices.len * 3) setValues(result.data["position"], pos) setValues(result.data["color"], col) diff -r 9767dec81193 -r 2ca938595aea src/semicongine/renderer.nim --- a/src/semicongine/renderer.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/renderer.nim Mon May 15 00:34:00 2023 +0700 @@ -101,13 +101,14 @@ perLocationOffsets: Table[MemoryPerformanceHint, uint64] perLocationSizes: Table[MemoryPerformanceHint, uint64] bindingNumber = 0 + for hint in MemoryPerformanceHint: + perLocationOffsets[hint] = 0 + perLocationSizes[hint] = 0 for attribute in inputs: data.attributeLocation[attribute.name] = attribute.memoryPerformanceHint data.attributeBindingNumber[attribute.name] = bindingNumber inc bindingNumber # setup one buffer per attribute-location-type - if not (attribute.memoryPerformanceHint in perLocationSizes): - perLocationSizes[attribute.memoryPerformanceHint] = 0'u64 for mesh in allMeshes: perLocationSizes[attribute.memoryPerformanceHint] += mesh.dataSize(attribute.name) for memoryPerformanceHint, bufferSize in perLocationSizes.pairs: @@ -118,7 +119,6 @@ requireMappable=memoryPerformanceHint==PreferFastWrite, preferVRAM=true, ) - perLocationOffsets[memoryPerformanceHint] = 0 # fill vertex buffers var indexBufferOffset = 0'u64 @@ -127,8 +127,9 @@ for attribute in inputs: offsets.add (attribute.name, attribute.memoryPerformanceHint, perLocationOffsets[attribute.memoryPerformanceHint]) var (pdata, size) = mesh.getRawData(attribute.name) - data.vertexBuffers[attribute.memoryPerformanceHint].setData(pdata, size, perLocationOffsets[attribute.memoryPerformanceHint]) - perLocationOffsets[attribute.memoryPerformanceHint] += size + if pdata != nil: # no data + data.vertexBuffers[attribute.memoryPerformanceHint].setData(pdata, size, perLocationOffsets[attribute.memoryPerformanceHint]) + perLocationOffsets[attribute.memoryPerformanceHint] += size let indexed = mesh.indexType != None var drawable = Drawable( @@ -182,6 +183,9 @@ proc refreshMeshAttributeData(sceneData: var SceneData, mesh: Mesh, attribute: string) = debug &"Refreshing data on mesh {mesh} for {attribute}" + # ignore attributes that are not used in this shader + if not (attribute in sceneData.attributeLocation): + return var (pdata, size) = mesh.getRawData(attribute) let memoryPerformanceHint = sceneData.attributeLocation[attribute] let bindingNumber = sceneData.attributeBindingNumber[attribute] diff -r 9767dec81193 -r 2ca938595aea src/semicongine/resources.nim --- a/src/semicongine/resources.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/resources.nim Mon May 15 00:34:00 2023 +0700 @@ -5,9 +5,13 @@ import ./core import ./resources/image import ./resources/audio +import ./resources/mesh + +from ./entity import Entity, Scene export image export audio +export mesh type ResourceBundlingType = enum @@ -102,6 +106,15 @@ proc loadAudio*(path: string): Sound = loadResource_intern(path).readAU() +proc loadMesh*(path: string): Entity = + loadResource_intern(path).readglTF()[0].root + +proc loadScene*(path: string): Scene = + loadResource_intern(path).readglTF()[0] + +proc loadScenes*(path: string): seq[Scene] = + loadResource_intern(path).readglTF() + proc modList*(): seq[string] = modList_intern() diff -r 9767dec81193 -r 2ca938595aea src/semicongine/resources/audio.nim --- a/src/semicongine/resources/audio.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/resources/audio.nim Mon May 15 00:34:00 2023 +0700 @@ -42,7 +42,6 @@ # https://en.wikipedia.org/wiki/Au_file_format -# Returns sound data and samplerate proc readAU*(stream: Stream): Sound = var header: AuHeader @@ -66,3 +65,6 @@ stream.setPosition(int(header.dataOffset)) while not stream.atEnd(): result[].add stream.readSample(header.encoding, int(header.channels)) + +var a = 2'u32 +echo a diff -r 9767dec81193 -r 2ca938595aea src/semicongine/resources/mesh.nim --- a/src/semicongine/resources/mesh.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/resources/mesh.nim Mon May 15 00:34:00 2023 +0700 @@ -1,2 +1,237 @@ +import std/json +import std/tables +import std/sequtils +import std/strutils +import std/strformat +import std/streams + +# from ../entity import Entity, Scene, Component +import ../entity +import ../mesh +import ../core + + +type + glTFHeader = object + magic: uint32 + version: uint32 + length: uint32 + glTFData = object + structuredContent: JsonNode + binaryBufferData: seq[uint8] + +const + JSON_CHUNK = 0x4E4F534A + BINARY_CHUNK = 0x004E4942 + ACCESSOR_TYPE_MAP = { + 5120: Int8, + 5121: UInt8, + 5122: Int16, + 5123: UInt16, + 5125: UInt32, + 5126: Float32, + }.toTable + VERTEX_ATTRIBUTE_DATA = 34962 + INSTANCE_ATTRIBUTE_DATA = 34963 + +func getGPUType(accessor: JsonNode): DataType = + # TODO: no full support for all datatypes that glTF may provide + # semicongine/core/gpu_data should maybe generated with macros to allow for all combinations + debugecho accessor.pretty() + let componentType = ACCESSOR_TYPE_MAP[accessor["componentType"].getInt()] + let theType = accessor["type"].getStr() + case theType + of "SCALAR": + return componentType + of "VEC2": + case componentType + of UInt32: return Vec2U32 + of Float32: return Vec2F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") + of "VEC3": + case componentType + of UInt32: return Vec3U32 + of Float32: return Vec3F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") + of "VEC4": + case componentType + of UInt32: return Vec4U32 + of Float32: return Vec4F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") + of "MAT2": + case componentType + of Float32: return Vec4F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") + of "MAT3": + case componentType + of Float32: return Vec4F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") + of "MAT4": + case componentType + of Float32: return Vec4F32 + else: raise newException(Exception, &"Unsupported data type: {componentType} {theType}") +proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: var seq[uint8]): DataList = + result.thetype = accessor.getGPUType() + result.initData(uint32(accessor["count"].getInt())) + + let bufferView = root["bufferViews"][accessor["bufferView"].getInt()] + assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" + + if accessor.hasKey("sparse"): + raise newException(Exception, "Sparce accessors are currently not implemented") + + let accessorOffset = if accessor.hasKey("byteOffset"): accessor["byteOffset"].getInt() else: 0 + let length = bufferView["byteLength"].getInt() + let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset + var dstPointer = result.getRawData()[0] + + if bufferView.hasKey("byteStride"): + raise newException(Exception, "Congratulations, you try to test a feature (loading buffer data with stride attributes) that we have no idea where it is used and how it can be tested (need a coresponding *.glb file). Please open an issue so we can finish the implementation.") + # 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) + else: + copyMem(dstPointer, addr mainBuffer[bufferOffset], length) + +proc addPrimitive(mesh: var Mesh, root: JsonNode, primitiveNode: JsonNode, mainBuffer: var seq[uint8]) = + # TODO: material + if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4: + raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode") + + for attribute, accessor in primitiveNode["attributes"].pairs: + let data = root.getAccessorData(root["accessors"][accessor.getInt()], mainBuffer) + mesh.appendMeshData(attribute, data) + + if primitiveNode.hasKey("indices"): + assert mesh.indexType != None + let data = root.getAccessorData(root["accessors"][primitiveNode["indices"].getInt()], mainBuffer) + var tri: seq[uint32] + case data.thetype + of UInt16: + for entry in getValues[uint16](data): + tri.add uint32(entry) + if tri.len == 3: + mesh.appendIndicesData(tri[0], tri[1], tri[2]) + tri.setLen(0) + of UInt32: + for entry in getValues[uint32](data): + tri.add uint32(entry) + if tri.len == 3: + mesh.appendIndicesData(tri[0], tri[1], tri[2]) + tri.setLen(0) + else: + raise newException(Exception, &"Unsupported index data type: {data.thetype}") + +proc loadMesh(root: JsonNode, meshNode: JsonNode, mainBuffer: var seq[uint8]): Mesh = + result = new Mesh + result.instanceCount = 1 + + # check if and how we use indexes + var indexCount = 0 + let indexed = meshNode["primitives"][0].hasKey("indices") + if indexed: + for primitive in meshNode["primitives"]: + indexCount += root["accessors"][primitive["indices"].getInt()]["count"].getInt() + if indexCount < int(high(uint16)): + result.indexType = Small + else: + result.indexType = Big + else: + result.indexType = None + + # check we have the same attributes for all primitives + let attributes = meshNode["primitives"][0]["attributes"].keys.toSeq + for primitive in meshNode["primitives"]: + assert primitive["attributes"].keys.toSeq == attributes + + # prepare mesh attributes + for attribute, accessor in meshNode["primitives"][0]["attributes"].pairs: + result.setMeshData(attribute, DataList(thetype: root["accessors"][accessor.getInt()].getGPUType())) + + # add all mesh data + for primitive in meshNode["primitives"]: + result.addPrimitive(root, primitive, mainBuffer) + +proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8]): Entity = + var name = "" + if node.hasKey("name"): + name = node["name"].getStr() + result = newEntity(name) + + # transformation + if node.hasKey("matrix"): + for i in 0 .. node["matrix"].len: + result.transform.data[i] = node["matrix"][i].getFloat() + else: + var (t, r, s) = (Unit4F32, Unit4F32, Unit4F32) + if node.hasKey("translation"): + t = translate3d( + float32(node["translation"][0].getFloat()), + float32(node["translation"][1].getFloat()), + float32(node["translation"][2].getFloat()) + ) + if node.hasKey("rotation"): + t = rotate3d( + float32(node["rotation"][3].getFloat()), + newVec3f( + float32(node["rotation"][0].getFloat()), + float32(node["rotation"][1].getFloat()), + float32(node["rotation"][2].getFloat()) + ) + ) + if node.hasKey("scale"): + t = scale3d( + float32(node["scale"][0].getFloat()), + float32(node["scale"][1].getFloat()), + float32(node["scale"][2].getFloat()) + ) + result.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.add loadMesh(root, root["meshes"][node["mesh"].getInt()], mainBuffer) + +proc loadScene(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8]): Scene = + var rootEntity = newEntity("") + for nodeId in scenenode["nodes"]: + rootEntity.add loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer) + + newScene(scenenode["name"].getStr(), rootEntity) + +proc readglTF*(stream: Stream): seq[Scene] = + var + header: glTFHeader + data: glTFData + + for name, value in fieldPairs(header): + stream.read(value) + + assert header.magic == 0x46546C67 + assert header.version == 2 + + var chunkLength = stream.readUint32() + assert stream.readUint32() == JSON_CHUNK + data.structuredContent = parseJson(stream.readStr(int(chunkLength))) + + chunkLength = stream.readUint32() + assert stream.readUint32() == BINARY_CHUNK + data.binaryBufferData.setLen(chunkLength) + assert stream.readData(addr data.binaryBufferData[0], int(chunkLength)) == int(chunkLength) + + # check that the refered buffer is the same as the binary chunk + # external binary buffers are not supported + assert data.structuredContent["buffers"].len == 1 + assert not data.structuredContent["buffers"][0].hasKey("uri") + let bufferLenDiff = int(chunkLength) - data.structuredContent["buffers"][0]["byteLength"].getInt() + assert 0 <= bufferLenDiff <= 3 # binary buffer may be aligned to 4 bytes + + for scene in data.structuredContent["scenes"]: + result.add data.structuredContent.loadScene(scene, data.binaryBufferData) diff -r 9767dec81193 -r 2ca938595aea src/semicongine/vulkan/physicaldevice.nim --- a/src/semicongine/vulkan/physicaldevice.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/semicongine/vulkan/physicaldevice.nim Mon May 15 00:34:00 2023 +0700 @@ -103,7 +103,7 @@ assert surface.valid var presentation = VkBool32(false) checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(family.device.vk, family.index, surface, addr presentation) - return presentation + return bool(presentation) proc canDoTransfer*(family: QueueFamily): bool = VK_QUEUE_TRANSFER_BIT in family.flags diff -r 9767dec81193 -r 2ca938595aea src/vulkan_api/vulkan_api_generator.nim --- a/src/vulkan_api/vulkan_api_generator.nim Sat May 13 19:32:28 2023 +0700 +++ b/src/vulkan_api/vulkan_api_generator.nim Mon May 15 00:34:00 2023 +0700 @@ -422,7 +422,6 @@ "import std/logging", "import std/typetraits", "import std/macros", - "import std/private/digitsutils", "type", " VkHandle* = distinct uint", " VkNonDispatchableHandle* = distinct uint", @@ -661,9 +660,9 @@ for l in GLOBAL_COMMANDS: mainout.add procLoads[l] mainout.add "" - mainout.add "converter VkBool2NimBool*(a: VkBool32): bool = a > 0" + # produces error if enable both implicit converters + # mainout.add "converter VkBool2NimBool*(a: VkBool32): bool = a > 0" mainout.add "converter NimBool2VkBool*(a: bool): VkBool32 = VkBool32(a)" - mainout.add "proc `$`*(x: uint32): string {.raises: [].} = addInt(result, x)" writeFile outdir / &"api.nim", mainout.join("\n") diff -r 9767dec81193 -r 2ca938595aea tests/resources/default/test1.glb Binary file tests/resources/default/test1.glb has changed diff -r 9767dec81193 -r 2ca938595aea tests/test_mesh.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_mesh.nim Mon May 15 00:34:00 2023 +0700 @@ -0,0 +1,40 @@ +import std/times + +import semicongine + +proc main() = + var scene = loadScene("test1.glb") + # var scene = loadScene("tutorialk-donat.glb") + + var engine = initEngine("Test meshes") + const + vertexInput = @[attr[Vec3f]("POSITION", memoryPerformanceHint=PreferFastRead)] + fragOutput = @[attr[Vec4f]("color")] + uniforms = @[attr[Mat4]("transform")] + vertexCode = compileGlslShader( + stage=VK_SHADER_STAGE_VERTEX_BIT, + inputs=vertexInput, + uniforms=uniforms, + main="""gl_Position = vec4(POSITION * 0.2, 1.0) * Uniforms.transform;""" + ) + fragmentCode = compileGlslShader( + stage=VK_SHADER_STAGE_FRAGMENT_BIT, + outputs=fragOutput, + uniforms=uniforms, + main=""" +color = vec4(61/255, 43/255, 31/255, 1); +""" + ) + engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode)) + engine.addScene(scene, vertexInput) + let rotateAxis = newVec3f(1, 1, 1) + scene.addShaderGlobal("transform", rotate3d(0'f32, rotateAxis)) + var t = cpuTime() + while engine.updateInputs() == Running and not engine.keyIsDown(Escape): + scene.setShaderGlobal("transform", rotate3d(float32(cpuTime() - t), rotateAxis)) + engine.renderScene(scene) + engine.destroy() + + +when isMainModule: + main()