Mercurial > games > semicongine
comparison semiconginev2/old/mesh.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 17 Jul 2024 21:01:37 +0700 |
| parents | semicongine/old/mesh.nim@a3eb305bcac2 |
| children |
comparison
equal
deleted
inserted
replaced
| 1217:f819a874058f | 1218:56781cc0fc7c |
|---|---|
| 1 import std/hashes | |
| 2 import std/options | |
| 3 import std/typetraits | |
| 4 import std/tables | |
| 5 import std/strformat | |
| 6 import std/enumerate | |
| 7 import std/strutils | |
| 8 import std/sequtils | |
| 9 | |
| 10 import ./core | |
| 11 import ./collision | |
| 12 import ./material | |
| 13 | |
| 14 const DEFAULT_POSITION_ATTRIBUTE = "position" | |
| 15 | |
| 16 var instanceCounter* = 0 | |
| 17 | |
| 18 type | |
| 19 MeshIndexType* = enum | |
| 20 None | |
| 21 Tiny # up to 2^8 vertices # TODO: need to check and enable support for this | |
| 22 Small # up to 2^16 vertices | |
| 23 Big # up to 2^32 vertices | |
| 24 MeshObject* = object | |
| 25 name*: string | |
| 26 vertexCount*: int | |
| 27 case indexType*: MeshIndexType | |
| 28 of None: discard | |
| 29 of Tiny: tinyIndices*: seq[array[3, uint8]] | |
| 30 of Small: smallIndices*: seq[array[3, uint16]] | |
| 31 of Big: bigIndices*: seq[array[3, uint32]] | |
| 32 material*: MaterialData | |
| 33 transform*: Mat4 = Unit4 | |
| 34 instanceTransforms*: seq[Mat4] | |
| 35 applyMeshTransformToInstances*: bool = true # if true, the transform attribute for the shader will apply the instance transform AND the mesh transform, to each instance | |
| 36 visible*: bool = true | |
| 37 transformCache: seq[Mat4] | |
| 38 vertexData: Table[string, DataList] | |
| 39 instanceData: Table[string, DataList] | |
| 40 dirtyAttributes: seq[string] | |
| 41 Mesh* = ref MeshObject | |
| 42 | |
| 43 func Material*(mesh: MeshObject): MaterialData = | |
| 44 mesh.material | |
| 45 | |
| 46 func `material=`*(mesh: var MeshObject, material: MaterialData) = | |
| 47 for name, theType in material.theType.vertexAttributes: | |
| 48 if mesh.vertexData.contains(name): | |
| 49 assert mesh.vertexData[name].theType == theType, &"{material.theType} expected vertex attribute '{name}' to be '{theType}' but it is {mesh.vertexData[name].theType}" | |
| 50 else: | |
| 51 assert false, &"Mesh '{mesh.name}' is missing required vertex attribute '{name}: {theType}' for {material.theType}" | |
| 52 for name, theType in material.theType.instanceAttributes: | |
| 53 if name in [TRANSFORM_ATTRIB, MATERIALINDEX_ATTRIBUTE]: | |
| 54 continue | |
| 55 if mesh.instanceData.contains(name): | |
| 56 assert mesh.instanceData[name].theType == theType, &"{material.theType} expected instance attribute '{name}' to be '{theType}' but it is {mesh.instanceData[name].theType}" | |
| 57 else: | |
| 58 assert false, &"Mesh '{mesh.name}' is missing required instance attribute '{name}: {theType}' for {material.theType}" | |
| 59 mesh.material = material | |
| 60 | |
| 61 func InstanceCount*(mesh: MeshObject): int = | |
| 62 mesh.instanceTransforms.len | |
| 63 | |
| 64 func IndicesCount*(mesh: MeshObject): int = | |
| 65 ( | |
| 66 case mesh.indexType | |
| 67 of None: 0 | |
| 68 of Tiny: mesh.tinyIndices.len | |
| 69 of Small: mesh.smallIndices.len | |
| 70 of Big: mesh.bigIndices.len | |
| 71 ) * 3 | |
| 72 | |
| 73 func `$`*(mesh: MeshObject): string = | |
| 74 if mesh.indexType == None: | |
| 75 &"Mesh('{mesh.name}', vertexCount: {mesh.vertexCount}, instanceCount: {mesh.InstanceCount}, vertexData: {mesh.vertexData.keys().toSeq()}, instanceData: {mesh.instanceData.keys().toSeq()}, indexType: {mesh.indexType}, material: {mesh.material})" | |
| 76 else: | |
| 77 &"Mesh('{mesh.name}', vertexCount: {mesh.vertexCount}, indexCount: {mesh.IndicesCount}, instanceCount: {mesh.InstanceCount}, vertexData: {mesh.vertexData.keys().toSeq()}, instanceData: {mesh.instanceData.keys().toSeq()}, indexType: {mesh.indexType}, material: {mesh.material})" | |
| 78 func `$`*(mesh: Mesh): string = | |
| 79 $mesh[] | |
| 80 | |
| 81 func VertexAttributes*(mesh: MeshObject): seq[string] = | |
| 82 mesh.vertexData.keys.toSeq | |
| 83 | |
| 84 func InstanceAttributes*(mesh: MeshObject): seq[string] = | |
| 85 mesh.instanceData.keys.toSeq | |
| 86 | |
| 87 func Attributes*(mesh: MeshObject): seq[string] = | |
| 88 mesh.VertexAttributes & mesh.InstanceAttributes | |
| 89 | |
| 90 func hash*(mesh: Mesh): Hash = | |
| 91 hash(cast[ptr MeshObject](mesh)) | |
| 92 | |
| 93 converter ToVulkan*(indexType: MeshIndexType): VkIndexType = | |
| 94 case indexType: | |
| 95 of None: VK_INDEX_TYPE_NONE_KHR | |
| 96 of Tiny: VK_INDEX_TYPE_UINT8_EXT | |
| 97 of Small: VK_INDEX_TYPE_UINT16 | |
| 98 of Big: VK_INDEX_TYPE_UINT32 | |
| 99 | |
| 100 proc InitVertexAttribute*[T](mesh: var MeshObject, attribute: string, value: seq[T]) = | |
| 101 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 102 mesh.vertexData[attribute] = InitDataList(thetype = GetDataType[T]()) | |
| 103 mesh.vertexData[attribute].SetLen(mesh.vertexCount) | |
| 104 mesh.vertexData[attribute] = value | |
| 105 proc InitVertexAttribute*[T](mesh: var MeshObject, attribute: string, value: T) = | |
| 106 InitVertexAttribute(mesh, attribute, newSeqWith(mesh.vertexCount, value)) | |
| 107 proc InitVertexAttribute*[T](mesh: var MeshObject, attribute: string) = | |
| 108 InitVertexAttribute(mesh = mesh, attribute = attribute, value = default(T)) | |
| 109 proc InitVertexAttribute*(mesh: var MeshObject, attribute: string, datatype: DataType) = | |
| 110 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 111 mesh.vertexData[attribute] = InitDataList(thetype = datatype) | |
| 112 mesh.vertexData[attribute].SetLen(mesh.vertexCount) | |
| 113 proc InitVertexAttribute*(mesh: var MeshObject, attribute: string, data: DataList) = | |
| 114 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 115 mesh.vertexData[attribute] = data | |
| 116 | |
| 117 | |
| 118 proc InitInstanceAttribute*[T](mesh: var MeshObject, attribute: string, value: seq[T]) = | |
| 119 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 120 mesh.instanceData[attribute] = InitDataList(thetype = GetDataType[T]()) | |
| 121 mesh.instanceData[attribute].SetLen(mesh.InstanceCount) | |
| 122 mesh.instanceData[attribute] = value | |
| 123 proc InitInstanceAttribute*[T](mesh: var MeshObject, attribute: string, value: T) = | |
| 124 InitInstanceAttribute(mesh, attribute, newSeqWith(mesh.InstanceCount, value)) | |
| 125 proc InitInstanceAttribute*[T](mesh: var MeshObject, attribute: string) = | |
| 126 InitInstanceAttribute(mesh = mesh, attribute = attribute, value = default(T)) | |
| 127 proc InitInstanceAttribute*(mesh: var MeshObject, attribute: string, datatype: DataType) = | |
| 128 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 129 mesh.instanceData[attribute] = InitDataList(thetype = datatype) | |
| 130 mesh.instanceData[attribute].SetLen(mesh.InstanceCount) | |
| 131 proc InitInstanceAttribute*(mesh: var MeshObject, attribute: string, data: DataList) = | |
| 132 assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute) | |
| 133 mesh.instanceData[attribute] = data | |
| 134 | |
| 135 proc NewMesh*( | |
| 136 positions: openArray[Vec3f], | |
| 137 indices: openArray[array[3, uint32|uint16|uint8]], | |
| 138 colors: openArray[Vec4f] = [], | |
| 139 uvs: openArray[Vec2f] = [], | |
| 140 transform: Mat4 = Unit4, | |
| 141 instanceTransforms: openArray[Mat4] = [Unit4], | |
| 142 material = EMPTY_MATERIAL.InitMaterialData(), | |
| 143 autoResize = true, | |
| 144 name: string = "" | |
| 145 ): Mesh = | |
| 146 assert colors.len == 0 or colors.len == positions.len | |
| 147 assert uvs.len == 0 or uvs.len == positions.len | |
| 148 var theName = name | |
| 149 if theName == "": | |
| 150 theName = &"mesh-{instanceCounter}" | |
| 151 inc instanceCounter | |
| 152 | |
| 153 # determine index type (uint8, uint16, uint32) | |
| 154 var indexType = None | |
| 155 if indices.len > 0: | |
| 156 indexType = Big | |
| 157 if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support | |
| 158 indexType = Tiny | |
| 159 elif autoResize and uint32(positions.len) < uint32(high(uint16)): | |
| 160 indexType = Small | |
| 161 | |
| 162 result = Mesh( | |
| 163 name: theName, | |
| 164 indexType: indexType, | |
| 165 vertexCount: positions.len, | |
| 166 instanceTransforms: @instanceTransforms, | |
| 167 transform: transform, | |
| 168 ) | |
| 169 | |
| 170 result[].InitVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, positions.toSeq) | |
| 171 if colors.len > 0: result[].InitVertexAttribute("color", colors.toSeq) | |
| 172 if uvs.len > 0: result[].InitVertexAttribute("uv", uvs.toSeq) | |
| 173 | |
| 174 # assert all indices are valid | |
| 175 for i in indices: | |
| 176 assert int(i[0]) < result[].vertexCount | |
| 177 assert int(i[1]) < result[].vertexCount | |
| 178 assert int(i[2]) < result[].vertexCount | |
| 179 | |
| 180 # cast index values to appropiate type | |
| 181 if result[].indexType == Tiny and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support | |
| 182 for i, tri in enumerate(indices): | |
| 183 result[].tinyIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])] | |
| 184 elif result[].indexType == Small and uint32(positions.len) < uint32(high(uint16)): | |
| 185 for i, tri in enumerate(indices): | |
| 186 result[].smallIndices.add [uint16(tri[0]), uint16(tri[1]), uint16(tri[2])] | |
| 187 elif result[].indexType == Big: | |
| 188 for i, tri in enumerate(indices): | |
| 189 result[].bigIndices.add [uint32(tri[0]), uint32(tri[1]), uint32(tri[2])] | |
| 190 `material=`(result[], material) | |
| 191 | |
| 192 proc NewMesh*( | |
| 193 positions: openArray[Vec3f], | |
| 194 colors: openArray[Vec4f] = [], | |
| 195 uvs: openArray[Vec2f] = [], | |
| 196 transform: Mat4 = Unit4, | |
| 197 instanceTransforms: openArray[Mat4] = [Unit4], | |
| 198 material = EMPTY_MATERIAL.InitMaterialData(), | |
| 199 name: string = "", | |
| 200 ): Mesh = | |
| 201 NewMesh( | |
| 202 positions = positions, | |
| 203 indices = newSeq[array[3, uint16]](), | |
| 204 colors = colors, | |
| 205 uvs = uvs, | |
| 206 transform = transform, | |
| 207 instanceTransforms = instanceTransforms, | |
| 208 material = material, | |
| 209 name = name, | |
| 210 ) | |
| 211 | |
| 212 func AttributeSize*(mesh: MeshObject, attribute: string): uint64 = | |
| 213 if mesh.vertexData.contains(attribute): | |
| 214 mesh.vertexData[attribute].Size | |
| 215 elif mesh.instanceData.contains(attribute): | |
| 216 mesh.instanceData[attribute].Size | |
| 217 else: | |
| 218 0 | |
| 219 | |
| 220 func AttributeType*(mesh: MeshObject, attribute: string): DataType = | |
| 221 if mesh.vertexData.contains(attribute): | |
| 222 mesh.vertexData[attribute].theType | |
| 223 elif mesh.instanceData.contains(attribute): | |
| 224 mesh.instanceData[attribute].theType | |
| 225 else: | |
| 226 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 227 | |
| 228 func IndexSize*(mesh: MeshObject): uint64 = | |
| 229 case mesh.indexType | |
| 230 of None: 0'u64 | |
| 231 of Tiny: uint64(mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0))) | |
| 232 of Small: uint64(mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0))) | |
| 233 of Big: uint64(mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0))) | |
| 234 | |
| 235 func rawData[T: seq](value: T): (pointer, uint64) = | |
| 236 ( | |
| 237 pointer(addr(value[0])), | |
| 238 uint64(sizeof(get(genericParams(typeof(value)), 0)) * value.len) | |
| 239 ) | |
| 240 | |
| 241 func GetRawIndexData*(mesh: MeshObject): (pointer, uint64) = | |
| 242 case mesh.indexType: | |
| 243 of None: raise newException(Exception, "Trying to get index data for non-indexed mesh") | |
| 244 of Tiny: rawData(mesh.tinyIndices) | |
| 245 of Small: rawData(mesh.smallIndices) | |
| 246 of Big: rawData(mesh.bigIndices) | |
| 247 | |
| 248 func GetPointer*(mesh: var MeshObject, attribute: string): pointer = | |
| 249 if mesh.vertexData.contains(attribute): | |
| 250 mesh.vertexData[attribute].GetPointer() | |
| 251 elif mesh.instanceData.contains(attribute): | |
| 252 mesh.instanceData[attribute].GetPointer() | |
| 253 else: | |
| 254 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 255 | |
| 256 proc GetAttribute[T: GPUType|int|uint|float](mesh: MeshObject, attribute: string): ref seq[T] = | |
| 257 if mesh.vertexData.contains(attribute): | |
| 258 mesh.vertexData[attribute][T] | |
| 259 elif mesh.instanceData.contains(attribute): | |
| 260 mesh.instanceData[attribute][T] | |
| 261 else: | |
| 262 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 263 | |
| 264 proc GetAttribute[T: GPUType|int|uint|float](mesh: MeshObject, attribute: string, i: int): T = | |
| 265 if mesh.vertexData.contains(attribute): | |
| 266 mesh.vertexData[attribute][i, T] | |
| 267 elif mesh.instanceData.contains(attribute): | |
| 268 mesh.instanceData[attribute][i, T] | |
| 269 else: | |
| 270 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 271 | |
| 272 template `[]`*(mesh: MeshObject, attribute: string, t: typedesc): ref seq[t] = | |
| 273 GetAttribute[t](mesh, attribute) | |
| 274 template `[]`*(mesh: MeshObject, attribute: string, i: int, t: typedesc): untyped = | |
| 275 GetAttribute[t](mesh, attribute, i) | |
| 276 template `[]=`*[T](mesh: MeshObject, attribute: string, value: seq[T]) = | |
| 277 GetAttribute[t](mesh, attribute) | |
| 278 template `[]=`*[T](mesh: MeshObject, attribute: string, i: int, value: T) = | |
| 279 GetAttribute[t](mesh, attribute, i) | |
| 280 | |
| 281 template `[]`*(mesh: Mesh, attribute: string, t: typedesc): ref seq[t] = | |
| 282 mesh[][attribute, t] | |
| 283 template `[]`*(mesh: Mesh, attribute: string, i: int, t: typedesc): untyped = | |
| 284 mesh[][attribute, i, t] | |
| 285 | |
| 286 proc UpdateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: DataList) = | |
| 287 if mesh.vertexData.contains(attribute): | |
| 288 assert data.len == mesh.vertexCount | |
| 289 assert data.theType == mesh.vertexData[attribute].theType | |
| 290 mesh.vertexData[attribute] = data | |
| 291 elif mesh.instanceData.contains(attribute): | |
| 292 assert data.len == mesh.InstanceCount | |
| 293 assert data.theType == mesh.instanceData[attribute].theType | |
| 294 mesh.instanceData[attribute] = data | |
| 295 else: | |
| 296 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 297 if not mesh.dirtyAttributes.contains(attribute): | |
| 298 mesh.dirtyAttributes.add attribute | |
| 299 | |
| 300 proc UpdateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: seq[T]) = | |
| 301 if mesh.vertexData.contains(attribute): | |
| 302 assert data.len == mesh.vertexCount | |
| 303 mesh.vertexData[attribute] = data | |
| 304 elif mesh.instanceData.contains(attribute): | |
| 305 assert data.len == mesh.InstanceCount | |
| 306 mesh.instanceData[attribute] = data | |
| 307 else: | |
| 308 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 309 if not mesh.dirtyAttributes.contains(attribute): | |
| 310 mesh.dirtyAttributes.add attribute | |
| 311 | |
| 312 proc UpdateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, i: int, value: T) = | |
| 313 if mesh.vertexData.contains(attribute): | |
| 314 assert i < mesh.vertexData[attribute].len | |
| 315 mesh.vertexData[attribute][i] = value | |
| 316 elif mesh.instanceData.contains(attribute): | |
| 317 assert i < mesh.instanceData[attribute].len | |
| 318 mesh.instanceData[attribute][i] = value | |
| 319 else: | |
| 320 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 321 if not mesh.dirtyAttributes.contains(attribute): | |
| 322 mesh.dirtyAttributes.add attribute | |
| 323 | |
| 324 proc `[]=`*[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: DataList) = | |
| 325 UpdateAttributeData[T](mesh, attribute, data) | |
| 326 proc `[]=`*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: DataList) = | |
| 327 UpdateAttributeData[t](mesh[], attribute, data) | |
| 328 | |
| 329 proc `[]=`*[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: seq[T]) = | |
| 330 UpdateAttributeData[T](mesh, attribute, data) | |
| 331 proc `[]=`*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, data: seq[T]) = | |
| 332 UpdateAttributeData[T](mesh[], attribute, data) | |
| 333 | |
| 334 proc `[]=`*[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, value: T) = | |
| 335 UpdateAttributeData[T](mesh, attribute, newSeqWith(mesh.vertexCount, value)) | |
| 336 proc `[]=`*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, value: T) = | |
| 337 UpdateAttributeData[T](mesh[], attribute, newSeqWith(mesh.vertexCount, value)) | |
| 338 | |
| 339 proc `[]=`*[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, i: int, value: T) = | |
| 340 UpdateAttributeData[T](mesh, attribute, i, value) | |
| 341 proc `[]=`*[T: GPUType|int|uint|float](mesh: Mesh, attribute: string, i: int, value: T) = | |
| 342 UpdateAttributeData[T](mesh[], attribute, i, value) | |
| 343 | |
| 344 proc RemoveAttribute*(mesh: var MeshObject, attribute: string) = | |
| 345 if mesh.vertexData.contains(attribute): | |
| 346 mesh.vertexData.del(attribute) | |
| 347 elif mesh.instanceData.contains(attribute): | |
| 348 mesh.instanceData.del(attribute) | |
| 349 else: | |
| 350 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 351 | |
| 352 proc AppendIndicesData*(mesh: var MeshObject, v1, v2, v3: int) = | |
| 353 case mesh.indexType | |
| 354 of None: raise newException(Exception, "Mesh does not support indexed data") | |
| 355 of Tiny: mesh.tinyIndices.add([uint8(v1), uint8(v2), uint8(v3)]) | |
| 356 of Small: mesh.smallIndices.add([uint16(v1), uint16(v2), uint16(v3)]) | |
| 357 of Big: mesh.bigIndices.add([uint32(v1), uint32(v2), uint32(v3)]) | |
| 358 | |
| 359 proc UpdateInstanceTransforms*(mesh: var MeshObject, attribute: string) = | |
| 360 var currentTransforms: seq[Mat4] | |
| 361 if mesh.applyMeshTransformToInstances: | |
| 362 currentTransforms = mesh.instanceTransforms.mapIt(mesh.transform * it) | |
| 363 else: | |
| 364 currentTransforms = mesh.instanceTransforms | |
| 365 if currentTransforms != mesh.transformCache: | |
| 366 mesh[attribute] = currentTransforms | |
| 367 mesh.transformCache = currentTransforms | |
| 368 | |
| 369 proc RenameAttribute*(mesh: var MeshObject, oldname, newname: string) = | |
| 370 if mesh.vertexData.contains(oldname): | |
| 371 mesh.vertexData[newname] = mesh.vertexData[oldname] | |
| 372 mesh.vertexData.del oldname | |
| 373 elif mesh.instanceData.contains(oldname): | |
| 374 mesh.instanceData[newname] = mesh.vertexData[oldname] | |
| 375 mesh.instanceData.del oldname | |
| 376 else: | |
| 377 raise newException(Exception, &"Attribute {oldname} is not defined for mesh {mesh}") | |
| 378 | |
| 379 func DirtyAttributes*(mesh: MeshObject): seq[string] = | |
| 380 mesh.dirtyAttributes | |
| 381 | |
| 382 proc ClearDirtyAttributes*(mesh: var MeshObject) = | |
| 383 mesh.dirtyAttributes.reset | |
| 384 | |
| 385 proc SetShaderMaterialIndices*(mesh: var MeshObject, shadername: string, values: seq[uint16], attributeName = MATERIALINDEX_ATTRIBUTE) = | |
| 386 let indexAttribute = shadername & "_" & attributeName | |
| 387 assert values.len == mesh.InstanceCount, &"Mesh {mesh}: While trying to set shader material indices for shader '{shadername}': indices have len {values.len}, but instance count is {mesh.InstanceCount}" | |
| 388 mesh[indexAttribute] = values | |
| 389 | |
| 390 # MESH-TOOLS | |
| 391 | |
| 392 proc Transform*[T: GPUType](mesh: var MeshObject, attribute: string, transform: Mat4) = | |
| 393 if mesh.vertexData.contains(attribute): | |
| 394 for i in 0 ..< mesh.vertexData[attribute].len: | |
| 395 mesh.vertexData[attribute][i] = transform * mesh.vertexData[attribute][i, T] | |
| 396 elif mesh.instanceData.contains(attribute): | |
| 397 for i in 0 ..< mesh.instanceData[attribute].len: | |
| 398 mesh.instanceData[attribute][i] = transform * mesh.vertexData[attribute][i, T] | |
| 399 else: | |
| 400 raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}") | |
| 401 mesh.dirtyAttributes.add attribute | |
| 402 | |
| 403 func GetMeshPoints*(mesh: MeshObject, positionAttribute = DEFAULT_POSITION_ATTRIBUTE): seq[Vec3f] = | |
| 404 for p in mesh[positionAttribute, Vec3f][]: | |
| 405 result.add mesh.transform * p | |
| 406 | |
| 407 func GetCollider*(mesh: MeshObject, positionAttribute = DEFAULT_POSITION_ATTRIBUTE): Collider = | |
| 408 return mesh.GetMeshPoints(positionAttribute).CalculateCollider(Points) | |
| 409 | |
| 410 proc AsNonIndexedMesh*(mesh: MeshObject): MeshObject = | |
| 411 if mesh.indexType == None: | |
| 412 return mesh | |
| 413 | |
| 414 result = MeshObject( | |
| 415 vertexCount: mesh.IndicesCount, | |
| 416 indexType: None, | |
| 417 transform: mesh.transform, | |
| 418 instanceTransforms: mesh.instanceTransforms, | |
| 419 visible: mesh.visible, | |
| 420 ) | |
| 421 for attribute, datalist in mesh.vertexData.pairs: | |
| 422 result.InitVertexAttribute(attribute, datalist.theType) | |
| 423 for attribute, datalist in mesh.instanceData.pairs: | |
| 424 result.instanceData[attribute] = datalist.Copy() | |
| 425 var i = 0 | |
| 426 case mesh.indexType | |
| 427 of Tiny: | |
| 428 for indices in mesh.tinyIndices: | |
| 429 for attribute, value in mesh.vertexData.pairs: | |
| 430 result.vertexData[attribute].AppendFrom(i, mesh.vertexData[attribute], int(indices[0])) | |
| 431 result.vertexData[attribute].AppendFrom(i + 1, mesh.vertexData[attribute], int(indices[1])) | |
| 432 result.vertexData[attribute].AppendFrom(i + 2, mesh.vertexData[attribute], int(indices[2])) | |
| 433 i += 3 | |
| 434 of Small: | |
| 435 for indices in mesh.smallIndices: | |
| 436 for attribute, value in mesh.vertexData.pairs: | |
| 437 result.vertexData[attribute].AppendFrom(i, value, int(indices[0])) | |
| 438 result.vertexData[attribute].AppendFrom(i + 1, value, int(indices[1])) | |
| 439 result.vertexData[attribute].AppendFrom(i + 2, value, int(indices[2])) | |
| 440 i += 3 | |
| 441 of Big: | |
| 442 for indices in mesh.bigIndices: | |
| 443 for attribute, value in mesh.vertexData.pairs: | |
| 444 result.vertexData[attribute].AppendFrom(i, mesh.vertexData[attribute], int(indices[0])) | |
| 445 result.vertexData[attribute].AppendFrom(i + 1, mesh.vertexData[attribute], int(indices[1])) | |
| 446 result.vertexData[attribute].AppendFrom(i + 2, mesh.vertexData[attribute], int(indices[2])) | |
| 447 i += 3 | |
| 448 else: | |
| 449 discard | |
| 450 `material=`(result, mesh.material) | |
| 451 | |
| 452 | |
| 453 # GENERATORS ============================================================================ | |
| 454 | |
| 455 proc Rect*(width = 1'f32, height = 1'f32, color = "ffffffff", material = EMPTY_MATERIAL.InitMaterialData()): Mesh = | |
| 456 result = Mesh( | |
| 457 vertexCount: 4, | |
| 458 instanceTransforms: @[Unit4], | |
| 459 indexType: Small, | |
| 460 smallIndices: @[[0'u16, 1'u16, 2'u16], [2'u16, 3'u16, 0'u16]], | |
| 461 name: &"rect-{instanceCounter}", | |
| 462 ) | |
| 463 inc instanceCounter | |
| 464 | |
| 465 let | |
| 466 half_w = width / 2 | |
| 467 half_h = height / 2 | |
| 468 pos = @[NewVec3f(-half_w, -half_h), NewVec3f(half_w, -half_h), NewVec3f(half_w, half_h), NewVec3f(-half_w, half_h)] | |
| 469 c = ToRGBA(color) | |
| 470 | |
| 471 result[].InitVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, pos) | |
| 472 result[].InitVertexAttribute("color", @[c, c, c, c]) | |
| 473 result[].InitVertexAttribute("uv", @[NewVec2f(0, 0), NewVec2f(1, 0), NewVec2f(1, 1), NewVec2f(0, 1)]) | |
| 474 `material=`(result[], material) | |
| 475 | |
| 476 proc Tri*(width = 1'f32, height = 1'f32, color = "ffffffff", material = EMPTY_MATERIAL.InitMaterialData()): Mesh = | |
| 477 result = Mesh( | |
| 478 vertexCount: 3, | |
| 479 instanceTransforms: @[Unit4], | |
| 480 name: &"tri-{instanceCounter}", | |
| 481 ) | |
| 482 inc instanceCounter | |
| 483 let | |
| 484 half_w = width / 2 | |
| 485 half_h = height / 2 | |
| 486 colorVec = ToRGBA(color) | |
| 487 | |
| 488 result[].InitVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, @[NewVec3f(0, -half_h), NewVec3f(half_w, half_h), NewVec3f(-half_w, half_h)]) | |
| 489 result[].InitVertexAttribute("color", @[colorVec, colorVec, colorVec]) | |
| 490 `material=`(result[], material) | |
| 491 | |
| 492 proc CircleMesh*(nSegments: int): (seq[Vec3f], seq[array[3, uint16]]) = | |
| 493 let | |
| 494 rX = 0.5 | |
| 495 rY = 0.5 | |
| 496 step = (2'f32 * PI) / float32(nSegments) | |
| 497 result[0] = @[NewVec3f(0, 0), NewVec3f(rX, 0)] | |
| 498 for i in 1 .. nSegments: | |
| 499 result[0].add NewVec3f(cos(float32(i) * step) * rX, sin(float32(i) * step) * rY) | |
| 500 result[1].add [uint16(0), uint16(i), uint16(i + 1)] | |
| 501 | |
| 502 proc Circle*(width = 1'f32, height = 1'f32, nSegments = 12, color = "ffffffff", material = EMPTY_MATERIAL.InitMaterialData()): Mesh = | |
| 503 assert nSegments >= 3 | |
| 504 result = Mesh( | |
| 505 vertexCount: 3 + nSegments, | |
| 506 instanceTransforms: @[Unit4], | |
| 507 indexType: Small, | |
| 508 name: &"circle-{instanceCounter}", | |
| 509 ) | |
| 510 inc instanceCounter | |
| 511 | |
| 512 let | |
| 513 rX = width / 2 | |
| 514 rY = height / 2 | |
| 515 c = ToRGBA(color) | |
| 516 step = (2'f32 * PI) / float32(nSegments) | |
| 517 var | |
| 518 pos = @[NewVec3f(0, 0), NewVec3f(rX, 0)] | |
| 519 col = @[c, c] | |
| 520 uv = @[NewVec2f(0.5, 0.5), NewVec2f(rX, height / 2)] | |
| 521 for i in 1 .. nSegments: | |
| 522 pos.add NewVec3f(cos(float32(i) * step) * rX, sin(float32(i) * step) * rY) | |
| 523 col.add c | |
| 524 uv.add NewVec2f(cos(float32(i) * step) * 0.5 + 0.5, sin(float32(i) * step) * 0.5 + 0.5) | |
| 525 result[].smallIndices.add [uint16(0), uint16(i), uint16(i + 1)] | |
| 526 | |
| 527 result[].InitVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, pos) | |
| 528 result[].InitVertexAttribute("color", col) | |
| 529 result[].InitVertexAttribute("uv", uv) | |
| 530 `material=`(result[], material) | |
| 531 | |
| 532 proc CircleMesh*(width = 1'f32, height = 1'f32, nSegments = 12): (seq[Vec3f], seq[array[3, uint16]]) = | |
| 533 assert nSegments >= 3 | |
| 534 result[0] = newSeq[Vec3f](3 + nSegments) | |
| 535 | |
| 536 let | |
| 537 rX = width / 2 | |
| 538 rY = height / 2 | |
| 539 step = (2'f32 * PI) / float32(nSegments) | |
| 540 result[0][0] = NewVec3f(0, 0) | |
| 541 result[0][1] = NewVec3f(rX, 0) | |
| 542 for i in 1 .. nSegments: | |
| 543 result[0][i + 1] = NewVec3f(cos(float32(i) * step) * rX, sin(float32(i) * step) * rY) | |
| 544 result[1].add [uint16(0), uint16(i), uint16(i + 1)] | |
| 545 | |
| 546 proc Grid*(columns, rows: uint16, cellSize = 1.0'f32, color = "ffffffff", material = EMPTY_MATERIAL.InitMaterialData()): Mesh = | |
| 547 | |
| 548 result = Mesh( | |
| 549 vertexCount: int((rows + 1) * (columns + 1)), | |
| 550 instanceTransforms: @[Unit4], | |
| 551 indexType: Small, | |
| 552 name: &"grid-{instanceCounter}", | |
| 553 ) | |
| 554 inc instanceCounter | |
| 555 | |
| 556 let | |
| 557 color = ToRGBA(color) | |
| 558 center_offset_x = -(float32(columns) * cellSize) / 2'f32 | |
| 559 center_offset_y = -(float32(rows) * cellSize) / 2'f32 | |
| 560 var | |
| 561 pos: seq[Vec3f] | |
| 562 col: seq[Vec4f] | |
| 563 i = 0'u16 | |
| 564 for h in 0'u16 .. rows: | |
| 565 for w in 0'u16 .. columns: | |
| 566 pos.add NewVec3f(center_offset_x + float32(w) * cellSize, center_offset_y + float32(h) * cellSize) | |
| 567 col.add color | |
| 568 if w > 0 and h > 0: | |
| 569 result[].smallIndices.add [i, i - 1, i - rows - 2] | |
| 570 result[].smallIndices.add [i, i - rows - 2, i - rows - 1] | |
| 571 i.inc | |
| 572 | |
| 573 result[].InitVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, pos) | |
| 574 result[].InitVertexAttribute("color", col) | |
| 575 `material=`(result[], material) | |
| 576 | |
| 577 proc MergeMeshData*(a: var Mesh, b: Mesh) = | |
| 578 let originalOffset = a.vertexCount | |
| 579 a.vertexCount = a.vertexCount + b.vertexCount | |
| 580 assert a.indexType == b.indexType | |
| 581 for key in a.vertexData.keys: | |
| 582 assert key in b.vertexData, &"Mesh {b} is missing vertex data for '{key}'" | |
| 583 for (key, value) in b.vertexData.pairs: | |
| 584 a.vertexData[key].AppendValues(value) | |
| 585 | |
| 586 case a.indexType: | |
| 587 of None: | |
| 588 discard | |
| 589 of Tiny: | |
| 590 let offset = uint8(originalOffset) | |
| 591 for i in b.tinyIndices: | |
| 592 a.tinyIndices.add [i[0] + offset, i[1] + offset, i[2] + offset] | |
| 593 of Small: | |
| 594 let offset = uint16(originalOffset) | |
| 595 for i in b.smallIndices: | |
| 596 a.smallIndices.add [i[0] + offset, i[1] + offset, i[2] + offset] | |
| 597 of Big: | |
| 598 let offset = uint32(originalOffset) | |
| 599 for i in b.bigIndices: | |
| 600 a.bigIndices.add [i[0] + offset, i[1] + offset, i[2] + offset] | |
| 601 | |
| 602 # MESH TREES ============================================================================= | |
| 603 | |
| 604 type | |
| 605 MeshTree* = ref object | |
| 606 mesh*: Mesh | |
| 607 transform*: Mat4 = Unit4 | |
| 608 children*: seq[MeshTree] | |
| 609 | |
| 610 func toStringRec*(tree: MeshTree, theindent = 0): seq[string] = | |
| 611 if tree.mesh.isNil: | |
| 612 result.add "*" | |
| 613 else: | |
| 614 result.add indent($tree.mesh, theindent) | |
| 615 for child in tree.children: | |
| 616 result.add child.toStringRec(theindent + 4) | |
| 617 | |
| 618 func `$`*(tree: MeshTree): string = | |
| 619 toStringRec(tree).join("\n") | |
| 620 | |
| 621 | |
| 622 proc toSeq*(tree: MeshTree): seq[Mesh] = | |
| 623 var queue = @[tree] | |
| 624 while queue.len > 0: | |
| 625 var current = queue.pop | |
| 626 if not current.mesh.isNil: | |
| 627 result.add current.mesh | |
| 628 queue.add current.children | |
| 629 | |
| 630 proc updateTransforms*(tree: MeshTree, parentTransform = Unit4) = | |
| 631 let currentTransform = parentTransform * tree.transform | |
| 632 if not tree.mesh.isNil: | |
| 633 tree.mesh.transform = currentTransform | |
| 634 for child in tree.children: | |
| 635 child.updateTransforms(currentTransform) |
