Mercurial > games > semicongine
changeset 266:fd1a95f433d1
add: better material loading system, still far from great
author | Sam <sam@basx.dev> |
---|---|
date | Sun, 28 May 2023 17:52:03 +0700 |
parents | ad4f2b45410b |
children | a107d9ef1a1f |
files | src/semicongine/core/gpu_data.nim src/semicongine/core/imagetypes.nim src/semicongine/renderer.nim src/semicongine/resources/mesh.nim src/semicongine/scene.nim src/semicongine/vulkan/pipeline.nim |
diffstat | 6 files changed, 160 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/core/gpu_data.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/core/gpu_data.nim Sun May 28 17:52:03 2023 +0700 @@ -806,6 +806,54 @@ of Mat4F64: value.mat4f64[].add data.mat4f64[] else: raise newException(Exception, &"Unsupported data type for GPU data:" ) +func appendValue*(value: var DataList, data: DataValue) = + assert value.thetype == data.thetype + value.len += 1 + 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
--- a/src/semicongine/core/imagetypes.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/core/imagetypes.nim Sun May 28 17:52:03 2023 +0700 @@ -15,6 +15,7 @@ Image* = ref ImageObject Texture* = object + name*: string image*: Image sampler*: Sampler
--- a/src/semicongine/renderer.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/renderer.nim Sun May 28 17:52:03 2023 +0700 @@ -17,14 +17,13 @@ import ./scene import ./mesh -import ./text type SceneData = object drawables*: OrderedTable[Mesh, Drawable] vertexBuffers*: Table[MemoryPerformanceHint, Buffer] indexBuffer*: Buffer - uniformBuffers*: seq[Buffer] # one per frame-in-flight + 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] @@ -164,6 +163,7 @@ indexBufferOffset += size data.drawables[mesh] = drawable + #[ # extract textures var sampler = DefaultSampler() sampler.magnification = VK_FILTER_NEAREST @@ -174,11 +174,13 @@ data.textures[textbox.font.name] = @[ renderer.device.uploadTexture(Texture(image: textbox.font.fontAtlas, sampler: sampler)) ] + ]# - for name, textures in scene.textures.pairs: - data.textures[name] = @[] - for texture in textures: - data.textures[name].add renderer.device.uploadTexture(texture) + for material in scene.getMaterials(): + for textureName, texture in material.textures.pairs: + if not data.textures.hasKey(textureName): + data.textures[textureName] = @[] + data.textures[textureName].add renderer.device.uploadTexture(texture) # setup uniforms and samplers for subpass_i in 0 ..< renderer.renderPass.subpasses.len: @@ -187,8 +189,9 @@ for uniform in pipeline.uniforms: uniformBufferSize += uniform.size if uniformBufferSize > 0: + data.uniformBuffers[pipeline.vk] = newSeq[Buffer]() for frame_i in 0 ..< renderer.swapchain.inFlightFrames: - data.uniformBuffers.add renderer.device.createBuffer( + data.uniformBuffers[pipeline.vk].add renderer.device.createBuffer( size=uniformBufferSize, usage=[VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], requireMappable=true, @@ -204,7 +207,7 @@ data.descriptorPool = renderer.device.createDescriptorSetPool(poolsizes) - data.descriptorSets[pipeline.vk] = pipeline.setupDescriptors(data.descriptorPool, data.uniformBuffers, data.textures, inFlightFrames=renderer.swapchain.inFlightFrames) + data.descriptorSets[pipeline.vk] = pipeline.setupDescriptors(data.descriptorPool, data.uniformBuffers[pipeline.vk], data.textures, inFlightFrames=renderer.swapchain.inFlightFrames) for frame_i in 0 ..< renderer.swapchain.inFlightFrames: data.descriptorSets[pipeline.vk][frame_i].writeDescriptorSet() @@ -241,18 +244,19 @@ proc updateUniformData*(renderer: var Renderer, scene: var Scene) = assert scene in renderer.scenedata - if renderer.scenedata[scene].uniformBuffers.len == 0: - return - assert renderer.scenedata[scene].uniformBuffers[renderer.swapchain.currentInFlight].vk.valid - for i in 0 ..< renderer.renderPass.subpasses.len: for pipeline in renderer.renderPass.subpasses[i].pipelines.mitems: - var offset = 0'u64 - for uniform in pipeline.uniforms: - assert uniform.thetype == scene.shaderGlobals[uniform.name].thetype - let (pdata, size) = scene.shaderGlobals[uniform.name].getRawData() - renderer.scenedata[scene].uniformBuffers[renderer.swapchain.currentInFlight].setData(pdata, size, offset) - offset += size + if renderer.scenedata[scene].uniformBuffers[pipeline.vk].len != 0: + assert renderer.scenedata[scene].uniformBuffers[pipeline.vk][renderer.swapchain.currentInFlight].vk.valid + var offset = 0'u64 + for uniform in pipeline.uniforms: + if not scene.shaderGlobals.hasKey(uniform.name): + raise newException(Exception, &"Uniform '{uniform.name}' not found in scene shaderGlobals") + if uniform.thetype != scene.shaderGlobals[uniform.name].thetype: + raise newException(Exception, &"Uniform '{uniform.name}' has wrong type {uniform.thetype}, required is {scene.shaderGlobals[uniform.name].thetype}") + let (pdata, size) = scene.shaderGlobals[uniform.name].getRawData() + renderer.scenedata[scene].uniformBuffers[pipeline.vk][renderer.swapchain.currentInFlight].setData(pdata, size, offset) + offset += size proc render*(renderer: var Renderer, scene: var Scene) = assert scene in renderer.scenedata @@ -313,9 +317,10 @@ if data.indexBuffer.vk.valid: assert data.indexBuffer.vk.valid data.indexBuffer.destroy() - for buffer in data.uniformBuffers.mitems: - assert buffer.vk.valid - buffer.destroy() + for pipelineUniforms in data.uniformBuffers.mvalues: + for buffer in pipelineUniforms.mitems: + assert buffer.vk.valid + buffer.destroy() for textures in data.textures.mvalues: for texture in textures.mitems: texture.destroy()
--- a/src/semicongine/resources/mesh.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/resources/mesh.nim Sun May 28 17:52:03 2023 +0700 @@ -1,5 +1,4 @@ import std/strutils -import std/options import std/json import std/logging import std/tables @@ -13,6 +12,8 @@ import ./image +let DEFAULTEXTURE = Texture(image: newImage(1, 1, @[[255'u8, 255'u8, 255'u8, 255'u8]]), sampler: DefaultSampler()) + type glTFHeader = object magic: uint32 @@ -21,21 +22,6 @@ glTFData = object structuredContent: JsonNode binaryBufferData: seq[uint8] - glTFMaterial = object - color: Vec4f - colorTexture: Option[Texture] - colorTextureIndex: uint32 - metallic: float32 - roughness: float32 - metallicRoughnessTexture: Option[Texture] - metallicRoughnessTextureIndex: uint32 - normalTexture: Option[Texture] - normalTextureIndex: uint32 - occlusionTexture: Option[Texture] - occlusionTextureIndex: uint32 - emissiveTexture: Option[Texture] - emissiveTextureIndex: uint32 - emissiveFactor: Vec3f const JSON_CHUNK = 0x4E4F534A @@ -287,40 +273,63 @@ if sampler.hasKey("wrapT"): result.sampler.wrapModeT = SAMPLER_WRAP_MODE_MAP[sampler["wrapS"].getInt()] -proc loadMaterial(root: JsonNode, materialNode: JsonNode, mainBuffer: var seq[uint8]): glTFMaterial = - let defaultMaterial = glTFMaterial(color: newVec4f(1, 1, 1, 1)) - result = defaultMaterial +proc loadMaterial(root: JsonNode, materialNode: JsonNode, mainBuffer: var seq[uint8]): Material = + result.name = materialNode.getStr("name") + let pbr = materialNode["pbrMetallicRoughness"] + + # color + result.data["baseColorFactor"] = DataValue(thetype: Vec4F32) if pbr.hasKey("baseColorFactor"): - result.color[0] = pbr["baseColorFactor"][0].getFloat() - result.color[1] = pbr["baseColorFactor"][1].getFloat() - result.color[2] = pbr["baseColorFactor"][2].getFloat() - result.color[3] = pbr["baseColorFactor"][3].getFloat() - if pbr.hasKey("baseColorTexture"): - result.colorTexture = some(loadTexture(root, pbr["baseColorTexture"]["index"].getInt(), mainBuffer)) - result.colorTextureIndex = pbr["baseColorTexture"].getOrDefault("texCoord").getInt(0).uint32 - if pbr.hasKey("metallicRoughnessTexture"): - result.metallicRoughnessTexture = some(loadTexture(root, pbr["metallicRoughnessTexture"]["index"].getInt(), mainBuffer)) - result.metallicRoughnessTextureIndex = pbr["metallicRoughnessTexture"].getOrDefault("texCoord").getInt().uint32 - if pbr.hasKey("metallicFactor"): - result.metallic = pbr["metallicFactor"].getFloat() - if pbr.hasKey("roughnessFactor"): - result.roughness= pbr["roughnessFactor"].getFloat() + setValue(result.data["baseColorFactor"], newVec4f( + pbr["baseColorFactor"][0].getFloat(), + pbr["baseColorFactor"][1].getFloat(), + pbr["baseColorFactor"][2].getFloat(), + pbr["baseColorFactor"][3].getFloat(), + )) + else: + setValue(result.data["baseColorFactor"], newVec4f(1, 1, 1, 1)) + + # pbr material constants + for factor in ["metallicFactor", "roughnessFactor"]: + result.data[factor] = DataValue(thetype: Float32) + if pbr.hasKey(factor): + setValue(result.data[factor], float32(pbr[factor].getFloat())) + else: + setValue(result.data[factor], 0.5'f32) - if materialNode.hasKey("normalTexture"): - result.normalTexture = some(loadTexture(root, materialNode["normalTexture"]["index"].getInt(), mainBuffer)) - result.metallicRoughnessTextureIndex = materialNode["normalTexture"].getOrDefault("texCoord").getInt().uint32 - if materialNode.hasKey("occlusionTexture"): - result.occlusionTexture = some(loadTexture(root, materialNode["occlusionTexture"]["index"].getInt(), mainBuffer)) - result.occlusionTextureIndex = materialNode["occlusionTexture"].getOrDefault("texCoord").getInt().uint32 - if materialNode.hasKey("emissiveTexture"): - result.emissiveTexture = some(loadTexture(root, materialNode["emissiveTexture"]["index"].getInt(), mainBuffer)) - result.occlusionTextureIndex = materialNode["emissiveTexture"].getOrDefault("texCoord").getInt().uint32 - if materialNode.hasKey("roughnessFactor"): - result.roughness = materialNode["roughnessFactor"].getFloat() + # pbr material textures + for texture in ["baseColorTexture", "metallicRoughnessTexture"]: + if pbr.hasKey(texture): + result.textures[texture] = loadTexture(root, pbr[texture]["index"].getInt(), mainBuffer) + result.data[texture & "Index"] = DataValue(thetype: UInt8) + setValue(result.data[texture & "Index"], pbr[texture].getOrDefault("texCoord").getInt(0).uint8) + else: + result.textures[texture] = DEFAULTEXTURE + result.data[texture & "Index"] = DataValue(thetype: UInt8) + setValue(result.data[texture & "Index"], 0'u8) + + # generic material textures + for texture in ["normalTexture", "occlusionTexture", "emissiveTexture"]: + if materialNode.hasKey(texture): + result.textures[texture] = loadTexture(root, materialNode[texture]["index"].getInt(), mainBuffer) + result.data[texture & "Index"] = DataValue(thetype: UInt8) + setValue(result.data[texture & "Index"], materialNode[texture].getOrDefault("texCoord").getInt(0).uint8) + else: + result.textures[texture] = DEFAULTEXTURE + result.data[texture & "Index"] = DataValue(thetype: UInt8) + setValue(result.data[texture & "Index"], 0'u8) + + # emissiv color + result.data["emissiveFactor"] = DataValue(thetype: Vec3F32) if materialNode.hasKey("emissiveFactor"): - let em = materialNode["emissiveFactor"] - result.emissiveFactor = newVec3f(em[0].getFloat(), em[1].getFloat(), em[2].getFloat()) + setValue(result.data["emissiveFactor"], newVec3f( + materialNode["emissiveFactor"][0].getFloat(), + materialNode["emissiveFactor"][1].getFloat(), + materialNode["emissiveFactor"][2].getFloat(), + )) + else: + setValue(result.data["emissiveFactor"], newVec3f(1'f32, 1'f32, 1'f32)) proc readglTF*(stream: Stream): seq[Scene] = var @@ -353,60 +362,7 @@ for scene in data.structuredContent["scenes"]: var scene = data.structuredContent.loadScene(scene, data.binaryBufferData) - var - color: seq[Vec4f] - colorTexture: seq[Texture] - colorTextureIndex: seq[uint32] - metallic: seq[float32] - roughness: seq[float32] - metallicRoughnessTexture: seq[Texture] - metallicRoughnessTextureIndex: seq[uint32] - normalTexture: seq[Texture] - normalTextureIndex: seq[uint32] - occlusionTexture: seq[Texture] - occlusionTextureIndex: seq[uint32] - emissiveTexture: seq[Texture] - emissiveTextureIndex: seq[uint32] - emissiveFactor: seq[Vec3f] for materialNode in data.structuredContent["materials"]: - let m = loadMaterial(data.structuredContent, materialNode, data.binaryBufferData) - color.add m.color - if not m.colorTexture.isSome: - colorTexture.add m.colorTexture.get - colorTextureIndex.add m.colorTextureIndex - metallic.add m.metallic - roughness.add m.roughness - if not m.metallicRoughnessTexture.isSome: - metallicRoughnessTexture.add m.metallicRoughnessTexture.get - metallicRoughnessTextureIndex.add m.metallicRoughnessTextureIndex - if not m.normalTexture.isSome: - normalTexture.add m.normalTexture.get - normalTextureIndex.add m.normalTextureIndex - if not m.occlusionTexture.isSome: - occlusionTexture.add m.occlusionTexture.get - occlusionTextureIndex.add m.occlusionTextureIndex - if not m.emissiveTexture.isSome: - emissiveTexture.add m.emissiveTexture.get - emissiveTextureIndex.add m.emissiveTextureIndex - emissiveFactor.add m.emissiveFactor - - # material constants - if color.len > 0: scene.addShaderGlobalArray("material_color", color) - if colorTextureIndex.len > 0: scene.addShaderGlobalArray("material_color_texture_index", colorTextureIndex) - if metallic.len > 0: scene.addShaderGlobalArray("material_metallic", metallic) - if roughness.len > 0: scene.addShaderGlobalArray("material_roughness", roughness) - if metallicRoughnessTextureIndex.len > 0: scene.addShaderGlobalArray("material_metallic_roughness_texture_index", metallicRoughnessTextureIndex) - if normalTextureIndex.len > 0: scene.addShaderGlobalArray("material_normal_texture_index", normalTextureIndex) - if occlusionTextureIndex.len > 0: scene.addShaderGlobalArray("material_occlusion_texture_index", occlusionTextureIndex) - if emissiveTextureIndex.len > 0: scene.addShaderGlobalArray("material_emissive_texture_index", emissiveTextureIndex) - if emissiveFactor.len > 0: scene.addShaderGlobalArray("material_emissive_factor", emissiveFactor) - - # texture - if colorTexture.len > 0: scene.addTextures("material_color_texture", colorTexture) - if metallicRoughnessTexture.len > 0: scene.addTextures("material_metallic_roughness_texture", metallicRoughnessTexture) - if normalTexture.len > 0: scene.addTextures("material_normal_texture", normalTexture) - if occlusionTexture.len > 0: scene.addTextures("material_occlusion_texture", occlusionTexture) - if emissiveTexture.len > 0: scene.addTextures("material_emissive_texture", emissiveTexture) + scene.addMaterial loadMaterial(data.structuredContent, materialNode, data.binaryBufferData) result.add scene -
--- a/src/semicongine/scene.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/scene.nim Sun May 28 17:52:03 2023 +0700 @@ -1,4 +1,6 @@ import std/strformat +import std/sequtils +import std/algorithm import std/strutils import std/tables import std/hashes @@ -7,14 +9,19 @@ import ./core type - Component* = ref object of RootObj - entity*: Entity - Scene* = object name*: string root*: Entity shaderGlobals*: Table[string, DataList] - textures*: Table[string, seq[Texture]] + materials: seq[Material] + + Material* = object + name*: string + textures*: Table[string, Texture] + data*: Table[string, DataValue] + + Component* = ref object of RootObj + entity*: Entity Entity* = ref object of RootObj name*: string @@ -50,15 +57,26 @@ func setShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) = setValues[T](scene.shaderGlobals[name], value) -func addTextures*(scene: var Scene, name: string, textures: seq[Texture], interpolation=VK_FILTER_LINEAR) = - scene.textures[name] = textures - -func addTexture*(scene: var Scene, name: string, texture: Texture) = - scene.textures[name] = @[texture] +func appendShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) = + appendValues[T](scene.shaderGlobals[name], value) func newScene*(name: string, root: Entity): Scene = Scene(name: name, root: root) +func getMaterials*(scene: Scene): seq[Material] = scene.materials + +func addMaterial*(scene: var Scene, material: Material) = + if scene.materials.len > 0: + assert material.data.keys.toSeq.sorted() == scene.materials[0].data.keys.toSeq.sorted(), &"{material.data.keys.toSeq.sorted()} == {scene.materials[0].data.keys.toSeq.sorted()}" + else: + for name, value in material.data.pairs: + scene.shaderGlobals[name] = newDataList(thetype=value.thetype) + + for name, value in material.data.pairs: + scene.shaderGlobals[name].appendValue(value) + + scene.materials.add material + func hash*(scene: Scene): Hash = hash(scene.name)
--- a/src/semicongine/vulkan/pipeline.nim Sat May 27 13:45:03 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Sun May 28 17:52:03 2023 +0700 @@ -1,4 +1,5 @@ import std/tables +import std/sequtils import std/strformat import ../core @@ -49,7 +50,7 @@ offset += size elif descriptor.thetype == ImageSampler: if not (descriptor.name in textures): - raise newException(Exception, "Missing shader texture in scene: " & descriptor.name) + raise newException(Exception, &"Missing shader texture in scene: {descriptor.name}, available are {textures.keys.toSeq}") if uint32(textures[descriptor.name].len) != descriptor.count: raise newException(Exception, &"Incorrect number of textures in array for {descriptor.name}: has {textures[descriptor.name].len} but needs {descriptor.count}") for t in textures[descriptor.name]: