Mercurial > games > semicongine
comparison semiconginev2/gltf.nim @ 1248:317bb5a73606
did: continue on gltf importer
author | sam <sam@basx.dev> |
---|---|
date | Thu, 25 Jul 2024 20:23:54 +0700 |
parents | c15770761865 |
children | d83726af7abb |
comparison
equal
deleted
inserted
replaced
1247:c15770761865 | 1248:317bb5a73606 |
---|---|
1 type | 1 type |
2 GLTFMesh*[TMesh, TMaterial] = object | 2 GLTFMesh*[TMesh, TMaterial] = object |
3 scenes*: seq[seq[int]] # each scene has a seq of node indices | 3 scenes*: seq[seq[int]] # each scene has a seq of node indices |
4 nodes*: seq[seq[int]] # each node has a seq of mesh indices | 4 nodes*: seq[seq[int]] # each node has a seq of mesh indices |
5 meshes*: seq[TMesh] | 5 meshes*: seq[seq[(TMesh, VkPrimitiveTopology)]] |
6 materials*: seq[TMaterial] | 6 materials*: seq[TMaterial] |
7 textures*: seq[Image[BGRA]] | 7 textures*: seq[Image[BGRA]] |
8 glTFHeader = object | 8 glTFHeader = object |
9 magic: uint32 | 9 magic: uint32 |
10 version: uint32 | 10 version: uint32 |
11 length: uint32 | 11 length: uint32 |
12 glTFData = object | 12 glTFData = object |
13 structuredContent: JsonNode | 13 structuredContent: JsonNode |
14 binaryBufferData: seq[uint8] | 14 binaryBufferData: seq[uint8] |
15 | 15 |
16 MaterialAttributeNames = object | 16 MaterialAttributeNames* = object |
17 # pbr | 17 # pbr |
18 baseColorTexture: string | 18 baseColorTexture*: string |
19 baseColorTextureUv: string | 19 baseColorTextureUv*: string |
20 baseColorFactor: string | 20 baseColorFactor*: string |
21 metallicRoughnessTexture: string | 21 metallicRoughnessTexture*: string |
22 metallicRoughnessTextureUv: string | 22 metallicRoughnessTextureUv*: string |
23 metallicFactor: string | 23 metallicFactor*: string |
24 roughnessFactor: string | 24 roughnessFactor*: string |
25 | 25 |
26 # other | 26 # other |
27 normalTexture: string | 27 normalTexture*: string |
28 normalTextureUv: string | 28 normalTextureUv*: string |
29 occlusionTexture: string | 29 occlusionTexture*: string |
30 occlusionTextureUv: string | 30 occlusionTextureUv*: string |
31 emissiveTexture: string | 31 emissiveTexture*: string |
32 emissiveTextureUv: string | 32 emissiveTextureUv*: string |
33 emissiveFactor: string | 33 emissiveFactor*: string |
34 | |
35 MeshAttributeNames* = object | |
36 POSITION*: string | |
37 NORMAL*: string | |
38 TANGENT*: string | |
39 TEXCOORD*: seq[string] | |
40 COLOR*: seq[string] | |
41 JOINTS*: seq[string] | |
42 WEIGHTS*: seq[string] | |
43 indices*: string | |
44 material*: string | |
34 | 45 |
35 #[ | 46 #[ |
36 static: | 47 static: |
37 let TypeIds = { | 48 let TypeIds = { |
38 int8: 5120, | 49 int8: 5120, |
69 SAMPLER_WRAP_MODE_MAP = { | 80 SAMPLER_WRAP_MODE_MAP = { |
70 33071: VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | 81 33071: VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
71 33648: VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, | 82 33648: VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, |
72 10497: VK_SAMPLER_ADDRESS_MODE_REPEAT | 83 10497: VK_SAMPLER_ADDRESS_MODE_REPEAT |
73 }.toTable | 84 }.toTable |
85 PRIMITIVE_MODE_MAP = [ | |
86 0: VK_PRIMITIVE_TOPOLOGY_POINT_LIST, | |
87 1: VK_PRIMITIVE_TOPOLOGY_LINE_LIST, | |
88 2: VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, # not correct, as mode 2 would be a loo, but vulkan has no concept of this | |
89 3: VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, | |
90 4: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
91 5: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, | |
92 6: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, | |
93 ] | |
74 | 94 |
75 #[ | 95 #[ |
76 proc getGPUType(accessor: JsonNode, attribute: string): DataType = | 96 proc getGPUType(accessor: JsonNode, attribute: string): DataType = |
77 # TODO: no full support for all datatypes that glTF may provide | 97 # TODO: no full support for all datatypes that glTF may provide |
78 # semicongine/core/gpu_data should maybe generated with macros to allow for all combinations | 98 # semicongine/core/gpu_data should maybe generated with macros to allow for all combinations |
119 | 139 |
120 if bufferView.hasKey("byteStride"): | 140 if bufferView.hasKey("byteStride"): |
121 raise newException(Exception, "Unsupported feature: byteStride in buffer view") | 141 raise newException(Exception, "Unsupported feature: byteStride in buffer view") |
122 copyMem(dstPointer, addr mainBuffer[bufferOffset], result.len) | 142 copyMem(dstPointer, addr mainBuffer[bufferOffset], result.len) |
123 | 143 |
124 #[ | 144 proc getAccessorData[T](root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): seq[T] = |
125 proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): DataList = | 145 result.setLen(accessor["count"].getInt()) |
126 result = InitDataList(thetype = accessor.getGPUType("??")) | |
127 result.SetLen(accessor["count"].getInt()) | |
128 | 146 |
129 let bufferView = root["bufferViews"][accessor["bufferView"].getInt()] | 147 let bufferView = root["bufferViews"][accessor["bufferView"].getInt()] |
130 assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" | 148 assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" |
131 | 149 |
132 if accessor.hasKey("sparse"): | 150 if accessor.hasKey("sparse"): |
133 raise newException(Exception, "Sparce accessors are currently not implemented") | 151 raise newException(Exception, "Sparce accessors are currently not supported") |
134 | 152 |
135 let accessorOffset = if accessor.hasKey("byteOffset"): accessor["byteOffset"].getInt() else: 0 | 153 let accessorOffset = if accessor.hasKey("byteOffset"): accessor["byteOffset"].getInt() else: 0 |
136 let length = bufferView["byteLength"].getInt() | 154 let length = bufferView["byteLength"].getInt() |
137 let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset | 155 let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset |
138 var dstPointer = result.GetPointer() | 156 var dstPointer = result.ToCPointer() |
139 | 157 |
140 if bufferView.hasKey("byteStride"): | 158 if bufferView.hasKey("byteStride"): |
141 warn "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)." | 159 warn "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)." |
142 # we don't support stride, have to convert stuff here... does this even work? | 160 # we don't support stride, have to convert stuff here... does this even work? |
143 for i in 0 ..< int(result.len): | 161 for i in 0 ..< int(result.len): |
144 copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView["byteStride"].getInt()], int(result.thetype.Size)) | 162 copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView["byteStride"].getInt()], result.len * sizeof(T)) |
145 dstPointer = cast[pointer](cast[uint](dstPointer) + result.thetype.Size) | 163 dstPointer = cast[typeof(dstPointer)](cast[uint](dstPointer) + (result.len * sizeof(T)).uint) |
146 else: | 164 else: |
147 copyMem(dstPointer, addr mainBuffer[bufferOffset], length) | 165 copyMem(dstPointer, addr mainBuffer[bufferOffset], length) |
148 ]# | |
149 | 166 |
150 proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[uint8]): Image[BGRA] = | 167 proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[uint8]): Image[BGRA] = |
151 | 168 |
152 let imageIndex = textureNode["source"].getInt() | 169 let imageIndex = textureNode["source"].getInt() |
153 | 170 |
195 elif gltfAttribute in ["metallicFactor", "roughnessFactor"]: | 212 elif gltfAttribute in ["metallicFactor", "roughnessFactor"]: |
196 value = pbr[gltfAttribute].getFloat() | 213 value = pbr[gltfAttribute].getFloat() |
197 else: | 214 else: |
198 {.error: "Unsupported gltf material attribute".} | 215 {.error: "Unsupported gltf material attribute".} |
199 | 216 |
217 proc loadPrimitive[TMesh](root: JsonNode, primitive: JsonNode, mapping: static MeshAttributeNames, mainBuffer: seq[uint8]): (TMesh, VkPrimitiveTopology) = | |
218 result[0] = TMesh() | |
219 result[1] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST | |
220 if primitive.hasKey("mode"): | |
221 result[1] = PRIMITIVE_MODE_MAP[primitive["mode"].getInt()] | |
222 | |
223 for name, value in fieldPairs(result[0]): | |
224 for gltfAttribute, mappedName in fieldPairs(mapping): | |
225 when gltfAttribute != "" and name == mappedName: | |
226 assert value is GPUData, "Attribute " & name & " must be of type GPUData" | |
227 #[ | |
228 when gltfAttribute == "indices": | |
229 if primitive.hasKey(gltfAttribute): | |
230 let accessor = primitive[gltfAttribute].getInt() | |
231 value.data = getAccessorData[elementType(value.data)](root, root["accessors"][accessor], mainBuffer) | |
232 elif gltfAttribute == "material": | |
233 if primitive.hasKey(gltfAttribute): | |
234 value.data = typeof(value.data)(primitive[gltfAttribute].getInt()) | |
235 else: | |
236 if primitive["attributes"].hasKey(gltfAttribute): | |
237 let accessor = primitive["attributes"][gltfAttribute].getInt() | |
238 value.data = getAccessorData[elementType(value.data)](root, root["accessors"][accessor], mainBuffer) | |
239 ]# | |
240 | |
241 #[ | |
242 var indexType = None | |
243 let indexed = primitive.hasKey("indices") | |
244 if indexed: | |
245 var indexCount = root["accessors"][primitive["indices"].getInt()]["count"].getInt() | |
246 if indexCount < int(high(uint16)): | |
247 indexType = Small | |
248 else: | |
249 indexType = Big | |
250 | |
251 for attribute, accessor in primitive["attributes"].pairs: | |
252 let data = root.getAccessorData(root["accessors"][accessor.getInt()], mainBuffer) | |
253 if result.vertexCount == 0: | |
254 result.vertexCount = data.len | |
255 assert data.len == result.vertexCount | |
256 result[].InitVertexAttribute(attribute.toLowerAscii, data) | |
257 | |
258 if primitive.hasKey("material"): | |
259 let materialId = primitive["material"].getInt() | |
260 result[].material = materials[materialId] | |
261 else: | |
262 result[].material = EMPTY_MATERIAL.InitMaterialData() | |
263 | |
264 if primitive.hasKey("indices"): | |
265 assert result[].indexType != None | |
266 let data = root.getAccessorData(root["accessors"][primitive["indices"].getInt()], mainBuffer) | |
267 var tri: seq[int] | |
268 case data.thetype | |
269 of UInt16: | |
270 for entry in data[uint16][]: | |
271 tri.add int(entry) | |
272 if tri.len == 3: | |
273 # FYI gltf uses counter-clockwise indexing | |
274 result[].AppendIndicesData(tri[0], tri[1], tri[2]) | |
275 tri.setLen(0) | |
276 of UInt32: | |
277 for entry in data[uint32][]: | |
278 tri.add int(entry) | |
279 if tri.len == 3: | |
280 # FYI gltf uses counter-clockwise indexing | |
281 result[].AppendIndicesData(tri[0], tri[1], tri[2]) | |
282 tri.setLen(0) | |
283 else: | |
284 raise newException(Exception, &"Unsupported index data type: {data.thetype}") | |
285 ]# | |
286 | |
200 | 287 |
201 #[ | 288 #[ |
202 | 289 |
203 proc loadMesh(meshname: string, root: JsonNode, primitiveNode: JsonNode, materials: seq[MaterialData], mainBuffer: seq[uint8]): Mesh = | 290 proc loadPrimitive(meshname: string, root: JsonNode, primitiveNode: JsonNode, materials: seq[MaterialData], mainBuffer: seq[uint8]): Mesh = |
204 if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4: | 291 if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4: |
205 raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode") | 292 raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode") |
206 | 293 |
207 var indexType = None | 294 var indexType = None |
208 let indexed = primitiveNode.hasKey("indices") | 295 let indexed = primitiveNode.hasKey("indices") |
263 result = MeshTree() | 350 result = MeshTree() |
264 # mesh | 351 # mesh |
265 if node.hasKey("mesh"): | 352 if node.hasKey("mesh"): |
266 let mesh = root["meshes"][node["mesh"].getInt()] | 353 let mesh = root["meshes"][node["mesh"].getInt()] |
267 for primitive in mesh["primitives"]: | 354 for primitive in mesh["primitives"]: |
268 result.children.add MeshTree(mesh: loadMesh(mesh["name"].getStr(), root, primitive, materials, mainBuffer)) | 355 result.children.add MeshTree(mesh: loadPrimitive(mesh["name"].getStr(), root, primitive, materials, mainBuffer)) |
269 | 356 |
270 # transformation | 357 # transformation |
271 if node.hasKey("matrix"): | 358 if node.hasKey("matrix"): |
272 var mat: Mat4 | 359 var mat: Mat4 |
273 for i in 0 ..< node["matrix"].len: | 360 for i in 0 ..< node["matrix"].len: |
314 | 401 |
315 ]# | 402 ]# |
316 | 403 |
317 proc ReadglTF*[TMesh, TMaterial]( | 404 proc ReadglTF*[TMesh, TMaterial]( |
318 stream: Stream, | 405 stream: Stream, |
319 baseColorFactor: static string = "", | 406 meshAttributesMapping: static MeshAttributeNames, |
320 emissiveFactor: static string = "", | 407 materialAttributesMapping: static MaterialAttributeNames, |
321 metallicFactor: static string = "", | |
322 roughnessFactor: static string = "", | |
323 baseColorTexture: static string = "", | |
324 metallicRoughnessTexture: static string = "", | |
325 normalTexture: static string = "", | |
326 occlusionTexture: static string = "", | |
327 emissiveTexture: static string = "", | |
328 ): GLTFMesh[TMesh, TMaterial] = | 408 ): GLTFMesh[TMesh, TMaterial] = |
329 const mapping = MaterialAttributeNames( | |
330 baseColorFactor: baseColorFactor, | |
331 emissiveFactor: emissiveFactor, | |
332 metallicFactor: metallicFactor, | |
333 roughnessFactor: roughnessFactor, | |
334 baseColorTexture: baseColorTexture, | |
335 metallicRoughnessTexture: metallicRoughnessTexture, | |
336 normalTexture: normalTexture, | |
337 occlusionTexture: occlusionTexture, | |
338 emissiveTexture: emissiveTexture, | |
339 ) | |
340 var | 409 var |
341 header: glTFHeader | 410 header: glTFHeader |
342 data: glTFData | 411 data: glTFData |
343 | 412 |
344 for name, value in fieldPairs(header): | 413 for name, value in fieldPairs(header): |
365 | 434 |
366 debug "Loading mesh: ", data.structuredContent.pretty | 435 debug "Loading mesh: ", data.structuredContent.pretty |
367 | 436 |
368 if "materials" in data.structuredContent: | 437 if "materials" in data.structuredContent: |
369 for materialnode in items(data.structuredContent["materials"]): | 438 for materialnode in items(data.structuredContent["materials"]): |
370 result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, data.binaryBufferData, mapping) | 439 result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, data.binaryBufferData, materialAttributesMapping) |
371 | 440 |
372 if "textures" in data.structuredContent: | 441 if "textures" in data.structuredContent: |
373 for texturenode in items(data.structuredContent["textures"]): | 442 for texturenode in items(data.structuredContent["textures"]): |
374 result.textures.add loadTexture(data.structuredContent, texturenode, data.binaryBufferData) | 443 result.textures.add loadTexture(data.structuredContent, texturenode, data.binaryBufferData) |
375 | 444 |
376 echo result | 445 if "meshes" in data.structuredContent: |
377 # for scenedata in data.structuredContent["scenes"]: | 446 for mesh in items(data.structuredContent["meshes"]): |
378 # result.add data.structuredContent.loadScene(scenedata, materials, data.binaryBufferData) | 447 var primitives: seq[(TMesh, VkPrimitiveTopology)] |
379 # | 448 for primitive in items(mesh["primitives"]): |
449 primitives.add loadPrimitive[TMesh](data.structuredContent, primitive, meshAttributesMapping, data.binaryBufferData) | |
450 result.meshes.add primitives | |
451 | |
452 echo "Textures:" | |
453 for t in result.textures: | |
454 echo " ", t | |
455 | |
456 echo "Materials:" | |
457 for m in result.materials: | |
458 echo " ", m | |
459 | |
460 echo "Meshes:" | |
461 for m in result.meshes: | |
462 echo " Primitives:" | |
463 for p in m: | |
464 echo " ", p[1], ": ", p[0] | |
465 | |
380 proc LoadMeshes*[TMesh, TMaterial]( | 466 proc LoadMeshes*[TMesh, TMaterial]( |
381 path: string, | 467 path: string, |
382 baseColorFactor: static string = "", | 468 meshAttributesMapping: static MeshAttributeNames, |
383 emissiveFactor: static string = "", | 469 materialAttributesMapping: static MaterialAttributeNames, |
384 metallicFactor: static string = "", | |
385 roughnessFactor: static string = "", | |
386 baseColorTexture: static string = "", | |
387 metallicRoughnessTexture: static string = "", | |
388 normalTexture: static string = "", | |
389 occlusionTexture: static string = "", | |
390 emissiveTexture: static string = "", | |
391 package = DEFAULT_PACKAGE | 470 package = DEFAULT_PACKAGE |
392 ): GLTFMesh[TMesh, TMaterial] = | 471 ): GLTFMesh[TMesh, TMaterial] = |
393 ReadglTF[TMesh, TMaterial]( | 472 ReadglTF[TMesh, TMaterial]( |
394 stream = loadResource_intern(path, package = package), | 473 stream = loadResource_intern(path, package = package), |
395 baseColorFactor = baseColorFactor, | 474 meshAttributesMapping = meshAttributesMapping, |
396 emissiveFactor = emissiveFactor, | 475 materialAttributesMapping = materialAttributesMapping, |
397 metallicFactor = metallicFactor, | |
398 roughnessFactor = roughnessFactor, | |
399 baseColorTexture = baseColorTexture, | |
400 metallicRoughnessTexture = metallicRoughnessTexture, | |
401 normalTexture = normalTexture, | |
402 occlusionTexture = occlusionTexture, | |
403 emissiveTexture = emissiveTexture, | |
404 ) | 476 ) |