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)