Mercurial > games > semicongine
diff semiconginev2/gltf.nim @ 1247:c15770761865
add: gltf loading test, gltf loading for materials
author | sam <sam@basx.dev> |
---|---|
date | Wed, 24 Jul 2024 23:26:34 +0700 |
parents | d594b1d07d49 |
children | 317bb5a73606 |
line wrap: on
line diff
--- a/semiconginev2/gltf.nim Wed Jul 24 20:12:19 2024 +0700 +++ b/semiconginev2/gltf.nim Wed Jul 24 23:26:34 2024 +0700 @@ -1,9 +1,10 @@ type - GLTFMesh[TMesh, TMaterial] = object - scenes: seq[int] - nodes: seq[int] - meshes: seq[TMesh] - materials: seq[TMaterial] + GLTFMesh*[TMesh, TMaterial] = object + scenes*: seq[seq[int]] # each scene has a seq of node indices + nodes*: seq[seq[int]] # each node has a seq of mesh indices + meshes*: seq[TMesh] + materials*: seq[TMaterial] + textures*: seq[Image[BGRA]] glTFHeader = object magic: uint32 version: uint32 @@ -13,20 +14,41 @@ binaryBufferData: seq[uint8] MaterialAttributeNames = object + # pbr + baseColorTexture: string + baseColorTextureUv: string baseColorFactor: string - emissiveFactor: string + metallicRoughnessTexture: string + metallicRoughnessTextureUv: string metallicFactor: string roughnessFactor: string - baseColorTexture: string - metallicRoughnessTexture: string + + # other normalTexture: string + normalTextureUv: string occlusionTexture: string + occlusionTextureUv: string emissiveTexture: string + emissiveTextureUv: string + emissiveFactor: string + +#[ +static: + let TypeIds = { + int8: 5120, + uint8: 5121, + int16: 5122, + uint16: 5123, + uint32: 5125, + float32: 5126, + }.toTable +]# const HEADER_MAGIC = 0x46546C67 JSON_CHUNK = 0x4E4F534A BINARY_CHUNK = 0x004E4942 + #[ ACCESSOR_TYPE_MAP = { 5120: Int8, 5121: UInt8, @@ -35,6 +57,7 @@ 5125: UInt32, 5126: Float32, }.toTable + ]# SAMPLER_FILTER_MODE_MAP = { 9728: VK_FILTER_NEAREST, 9729: VK_FILTER_LINEAR, @@ -48,18 +71,8 @@ 33648: VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, 10497: VK_SAMPLER_ADDRESS_MODE_REPEAT }.toTable - GLTF_MATERIAL_MAPPING = { - "color": "baseColorFactor", - "emissiveColor": "emissiveFactor", - "metallic": "metallicFactor", - "roughness", "roughnessFactor", - "baseTexture": "baseColorTexture", - "metallicRoughnessTexture": "metallicRoughnessTexture", - "normalTexture": "normalTexture", - "occlusionTexture": "occlusionTexture", - "emissiveTexture": "emissiveTexture", - }.toTable +#[ proc getGPUType(accessor: JsonNode, attribute: string): 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 @@ -95,6 +108,7 @@ case componentType of Float32: return Vec4F32 else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}") +]# proc getBufferViewData(bufferView: JsonNode, mainBuffer: seq[uint8], baseBufferOffset = 0): seq[uint8] = assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" @@ -107,6 +121,7 @@ raise newException(Exception, "Unsupported feature: byteStride in buffer view") copyMem(dstPointer, addr mainBuffer[bufferOffset], result.len) +#[ proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): DataList = result = InitDataList(thetype = accessor.getGPUType("??")) result.SetLen(accessor["count"].getInt()) @@ -130,113 +145,60 @@ dstPointer = cast[pointer](cast[uint](dstPointer) + result.thetype.Size) else: copyMem(dstPointer, addr mainBuffer[bufferOffset], length) +]# -proc loadImage(root: JsonNode, imageIndex: int, mainBuffer: seq[uint8]): Image[RGBAPixel] = +proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[uint8]): Image[BGRA] = + + let imageIndex = textureNode["source"].getInt() + if root["images"][imageIndex].hasKey("uri"): - raise newException(Exception, "Unsupported feature: Load images from external files") + raise newException(Exception, "Unsupported feature: Cannot load images from external files") + let imageType = root["images"][imageIndex]["mimeType"].getStr() + assert imageType == "image/png", "glTF loader currently only supports PNG" let bufferView = root["bufferViews"][root["images"][imageIndex]["bufferView"].getInt()] - let imgData = newStringStream(cast[string](getBufferViewData(bufferView, mainBuffer))) - - let imageType = root["images"][imageIndex]["mimeType"].getStr() - case imageType - of "image/bmp": - result = ReadBMP(imgData) - of "image/png": - result = ReadPNG(imgData) - else: - raise newException(Exception, "Unsupported feature: Load image of type " & imageType) - -proc loadTexture(root: JsonNode, textureIndex: int, mainBuffer: seq[uint8]): Texture = - let textureNode = root["textures"][textureIndex] - result = Texture(isGrayscale: false) - result.colorImage = loadImage(root, textureNode["source"].getInt(), mainBuffer) - result.name = root["images"][textureNode["source"].getInt()]["name"].getStr() - if result.name == "": - result.name = &"Texture{textureIndex}" + result = LoadImage[BGRA](getBufferViewData(bufferView, mainBuffer)) if textureNode.hasKey("sampler"): let sampler = root["samplers"][textureNode["sampler"].getInt()] if sampler.hasKey("magFilter"): - result.sampler.magnification = SAMPLER_FILTER_MODE_MAP[sampler["magFilter"].getInt()] + result.magInterpolation = SAMPLER_FILTER_MODE_MAP[sampler["magFilter"].getInt()] if sampler.hasKey("minFilter"): - result.sampler.minification = SAMPLER_FILTER_MODE_MAP[sampler["minFilter"].getInt()] + result.minInterpolation = SAMPLER_FILTER_MODE_MAP[sampler["minFilter"].getInt()] if sampler.hasKey("wrapS"): - result.sampler.wrapModeS = SAMPLER_WRAP_MODE_MAP[sampler["wrapS"].getInt()] + result.wrapU = SAMPLER_WRAP_MODE_MAP[sampler["wrapS"].getInt()] if sampler.hasKey("wrapT"): - result.sampler.wrapModeT = SAMPLER_WRAP_MODE_MAP[sampler["wrapS"].getInt()] + result.wrapV = SAMPLER_WRAP_MODE_MAP[sampler["wrapT"].getInt()] +proc getVec4f(node: JsonNode): Vec4f = + NewVec4f(node[0].getFloat(), node[1].getFloat(), node[2].getFloat(), node[3].getFloat()) proc loadMaterial[TMaterial]( root: JsonNode, materialNode: JsonNode, mainBuffer: seq[uint8], - mapping: MaterialAttributeNames + mapping: static MaterialAttributeNames ): TMaterial = - let pbr = materialNode["pbrMetallicRoughness"] - for glName, glValue in fieldPairs(mapping): - if glValue != "": - for name, value in fieldPairs(result): - when name == glName: - value = - - #[ - - # color - if defaultMaterial.attributes.contains("color"): - attributes["color"] = InitDataList(thetype = Vec4F32) - if pbr.hasKey(GLTF_MATERIAL_MAPPING["color"]): - attributes["color"] = @[NewVec4f( - pbr[GLTF_MATERIAL_MAPPING["color"]][0].getFloat(), - pbr[GLTF_MATERIAL_MAPPING["color"]][1].getFloat(), - pbr[GLTF_MATERIAL_MAPPING["color"]][2].getFloat(), - pbr[GLTF_MATERIAL_MAPPING["color"]][3].getFloat(), - )] - else: - attributes["color"] = @[NewVec4f(1, 1, 1, 1)] - - # pbr material values - for factor in ["metallic", "roughness"]: - if defaultMaterial.attributes.contains(factor): - attributes[factor] = InitDataList(thetype = Float32) - if pbr.hasKey(GLTF_MATERIAL_MAPPING[factor]): - attributes[factor] = @[float32(pbr[GLTF_MATERIAL_MAPPING[factor]].getFloat())] - else: - attributes[factor] = @[0.5'f32] + result = TMaterial() - # pbr material textures - for texture in ["baseTexture", "metallicRoughnessTexture"]: - if defaultMaterial.attributes.contains(texture): - attributes[texture] = InitDataList(thetype = TextureType) - # attributes[texture & "Index"] = InitDataList(thetype=UInt8) - if pbr.hasKey(GLTF_MATERIAL_MAPPING[texture]): - attributes[texture] = @[loadTexture(root, pbr[GLTF_MATERIAL_MAPPING[texture]]["index"].getInt(), mainBuffer)] - else: - attributes[texture] = @[EMPTY_TEXTURE] + let pbr = materialNode["pbrMetallicRoughness"] + for name, value in fieldPairs(result): + for gltfAttribute, mappedName in fieldPairs(mapping): + when gltfAttribute != "" and name == mappedName: + if pbr.hasKey(gltfAttribute): + when gltfAttribute.endsWith("Texture"): + value = typeof(value)(pbr[gltfAttribute]["index"].getInt()) + elif gltfAttribute.endsWith("TextureUv"): + value = typeof(pbr[gltfAttribute[0 ..< ^2]]["index"].getInt()) + elif gltfAttribute in ["baseColorFactor", "emissiveFactor"]: + value = pbr[gltfAttribute].getVec4f() + elif gltfAttribute in ["metallicFactor", "roughnessFactor"]: + value = pbr[gltfAttribute].getFloat() + else: + {.error: "Unsupported gltf material attribute".} - # generic material textures - for texture in ["normalTexture", "occlusionTexture", "emissiveTexture"]: - if defaultMaterial.attributes.contains(texture): - attributes[texture] = InitDataList(thetype = TextureType) - # attributes[texture & "Index"] = InitDataList(thetype=UInt8) - if materialNode.hasKey(GLTF_MATERIAL_MAPPING[texture]): - attributes[texture] = @[loadTexture(root, materialNode[texture]["index"].getInt(), mainBuffer)] - else: - attributes[texture] = @[EMPTY_TEXTURE] - # emissiv color - if defaultMaterial.attributes.contains("emissiveColor"): - attributes["emissiveColor"] = InitDataList(thetype = Vec3F32) - if materialNode.hasKey(GLTF_MATERIAL_MAPPING["emissiveColor"]): - attributes["emissiveColor"] = @[NewVec3f( - materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][0].getFloat(), - materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][1].getFloat(), - materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][2].getFloat(), - )] - else: - attributes["emissiveColor"] = @[NewVec3f(1'f32, 1'f32, 1'f32)] - ]# - +#[ proc loadMesh(meshname: string, root: JsonNode, primitiveNode: JsonNode, materials: seq[MaterialData], mainBuffer: seq[uint8]): Mesh = if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4: @@ -296,6 +258,7 @@ # TODO: getting from gltf to vulkan system is still messed up somehow, see other TODO Transform[Vec3f](result[], "position", Scale(1, -1, 1)) + proc loadNode(root: JsonNode, node: JsonNode, materials: seq[MaterialData], mainBuffer: var seq[uint8]): MeshTree = result = MeshTree() # mesh @@ -349,30 +312,30 @@ # result.transform = Scale(1, -1, 1) result.updateTransforms() + ]# -proc ReadglTF*[TMaterial, TMesh]( +proc ReadglTF*[TMesh, TMaterial]( stream: Stream, - attributeNames: MaterialAttributeNames, - baseColorFactor = "", - emissiveFactor = "", - metallicFactor = "", - roughnessFactor = "", - baseColorTexture = "", - metallicRoughnessTexture = "", - normalTexture = "", - occlusionTexture = "", - emissiveTexture = "", + baseColorFactor: static string = "", + emissiveFactor: static string = "", + metallicFactor: static string = "", + roughnessFactor: static string = "", + baseColorTexture: static string = "", + metallicRoughnessTexture: static string = "", + normalTexture: static string = "", + occlusionTexture: static string = "", + emissiveTexture: static string = "", ): GLTFMesh[TMesh, TMaterial] = - let mapping = MaterialAttributeNames( - baseColorFactor: baseColorFactor - emissiveFactor: emissiveFactor - metallicFactor: metallicFactor - roughnessFactor: roughnessFactor - baseColorTexture: baseColorTexture - metallicRoughnessTexture: metallicRoughnessTexture - normalTexture: normalTexture - occlusionTexture: occlusionTexture - emissiveTexture: emissiveTexture + const mapping = MaterialAttributeNames( + baseColorFactor: baseColorFactor, + emissiveFactor: emissiveFactor, + metallicFactor: metallicFactor, + roughnessFactor: roughnessFactor, + baseColorTexture: baseColorTexture, + metallicRoughnessTexture: metallicRoughnessTexture, + normalTexture: normalTexture, + occlusionTexture: occlusionTexture, + emissiveTexture: emissiveTexture, ) var header: glTFHeader @@ -402,9 +365,40 @@ debug "Loading mesh: ", data.structuredContent.pretty - var materials: seq[MaterialData] - for materialnode in data.structuredContent["materials"]: - result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, data.binaryBufferData, mapping) + if "materials" in data.structuredContent: + for materialnode in items(data.structuredContent["materials"]): + result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, data.binaryBufferData, mapping) + + if "textures" in data.structuredContent: + for texturenode in items(data.structuredContent["textures"]): + result.textures.add loadTexture(data.structuredContent, texturenode, data.binaryBufferData) - for scenedata in data.structuredContent["scenes"]: - result.add data.structuredContent.loadScene(scenedata, materials, data.binaryBufferData) + echo result + # for scenedata in data.structuredContent["scenes"]: + # result.add data.structuredContent.loadScene(scenedata, materials, data.binaryBufferData) + # +proc LoadMeshes*[TMesh, TMaterial]( + path: string, + baseColorFactor: static string = "", + emissiveFactor: static string = "", + metallicFactor: static string = "", + roughnessFactor: static string = "", + baseColorTexture: static string = "", + metallicRoughnessTexture: static string = "", + normalTexture: static string = "", + occlusionTexture: static string = "", + emissiveTexture: static string = "", + package = DEFAULT_PACKAGE +): GLTFMesh[TMesh, TMaterial] = + ReadglTF[TMesh, TMaterial]( + stream = loadResource_intern(path, package = package), + baseColorFactor = baseColorFactor, + emissiveFactor = emissiveFactor, + metallicFactor = metallicFactor, + roughnessFactor = roughnessFactor, + baseColorTexture = baseColorTexture, + metallicRoughnessTexture = metallicRoughnessTexture, + normalTexture = normalTexture, + occlusionTexture = occlusionTexture, + emissiveTexture = emissiveTexture, + )