-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathac_scene.lua
1503 lines (1338 loc) · 86.6 KB
/
ac_scene.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
__source 'lua/api_scene.cpp'
__allow 'scene'
require './ac_ray'
require './ac_display'
require './ac_render_enums'
require './ac_render_shader'
require './ac_extras_tracklines'
local _sp_scenep = {template = 'project.fx', __cache = {}, defaultBlendMode = render.BlendMode.BlendAccurate, delayed = true}
-- Mesh vertex related stuff:
ffi.cdef [[ typedef struct { vec3 pos; vec3 normal; vec2 uv; vec3 __extras; } lua_mesh_vertex; ]]
---Mesh vertex.
---@class ac.MeshVertex
---@field pos vec3
---@field normal vec3
---@field uv vec2
---@constructor fun(pos: vec3, normal: vec3, uv: vec2): ac.MeshVertex
ac.MeshVertex = ffi.metatype('lua_mesh_vertex', {
__len = function(s) return #s.pos end,
__index = {
---Creates new mesh vertex.
---@param pos vec3
---@param normal vec3
---@param uv vec2
---@return ac.MeshVertex
new = function(pos, normal, uv)
return ac.MeshVertex(pos, normal or vec3(0, 1, 0), uv or vec2())
end,
}
})
local __vecMeshVertices = __util.arrayType(ffi.typeof('lua_mesh_vertex'))
local __vecMeshIndices = __util.arrayType(ffi.typeof('uint16_t'))
--[[? if (ctx.ldoc) out(]]
---Buffer with mesh vertices. Contains `ac.MeshVertex` items.
---@class ac.VertexBuffer : ac.GenericList
---@constructor fun(size: nil|integer|ac.MeshVertex[] "Initial size or initializing values."): ac.VertexBuffer
---@param index integer @1-based index.
---@return ac.MeshVertex
function _ac_VertexBuffer:get(index) end
---@param index integer @1-based index.
---@param vertex ac.MeshVertex
function _ac_VertexBuffer:set(index, vertex) end
--[[) ?]]
function ac.VertexBuffer(size)
local ret = __vecMeshVertices(0, size)
if type(size) == 'number' then
ffi.C.lj_init_vertices__scene(ret.raw, size)
end
return ret
end
---Buffer with mesh indieces. Contains `integer` items (limited by 16 bits for AC to handle).
---@class ac.IndicesBuffer : ac.GenericList
---@constructor fun(size: nil|integer|integer[] "Initial size or initializing values."): ac.IndicesBuffer
function ac.IndicesBuffer(size)
return __vecMeshIndices(0, size)
end
---Collect list of textures in KN5 file.
---@param kn5Filename string
---@param filter string? @Texture names filter. Default value: `'?'`.
---@return string[]? @Returns `nil` if there is no such file, no access to it or the file is damaged.
function ac.collectKN5TextureNames(kn5Filename, filter)
return __util.native('kn5_texture_names', kn5Filename, filter)
end
---Collect list of material properties in KN5 file in a form of shader replacements config.
---@param kn5Filename string
---@param filter string? @Material names filter. Default value: `'?'`.
---@return string[]? @Returns `nil` if there is no such file, no access to it or the file is damaged.
function ac.collectKN5MaterialProperties(kn5Filename, filter)
return __util.native('kn5_material_props', kn5Filename, filter)
end
-- Scene references:
local aliveRefs = {}
local function cr(v, keepAlive)
if v == nil then return nil end
local r = ffi.gc(v, ffi.C.lj_noderef_gc__scene)
if keepAlive and not aliveRefs[r] then
aliveRefs[r] = true
end
return r
end
local function nf(v)
if type(v) == 'table' then
return '{' .. table.join(v, ', ') .. '}'
end
return v ~= nil and tostring(v) or ""
end
local function _emptyNodeRef()
return cr(ffi.C.lj_noderef_new__scene(nil, 1))
end
local function _passRefArgs(other)
if type(other) == 'table' then
ffi.C.lj_noderef_begin_arg_pass__scene()
for i = 2, #other do
ffi.C.lj_noderef_arg_pass__scene(other[i])
end
other = other[1]
end
return other
end
local _texBgSkip = rgbm(0,0,0,-1)
local _texBgOriginal = rgbm(-1,0,0,0)
local _vecUp = vec3(0, 1, 0)
local function __sip(s, i)
i = i + 1
if i <= s.__size then return i, s:at(i) end
end
---Reference to one or several objects in scene. Works similar to those jQuery things which would refer to one or
---several of webpage elements. Use methods like `ac.findNodes()` to get one. Once you have a reference to some nodes,
---you can load additional KN5s, create new nodes and such in it.
---Note: it might be beneficial in general to prefer methods like `ac.findNodes()` and `ac.findMeshes()` over `ac.findAny()`.
---Should be fewer surprises this way.
---@class ac.SceneReference
ffi.cdef [[ typedef struct { int __size; } noderef; ]]
ffi.metatype('noderef', {
__len = function(s) return s.__size end,
__tostring = function(s)
return string.format('ac.SceneReference<%d>', s.__size)
end,
__ipairs = function(s)
return __sip, s, 0
end,
__index = {
---Dispose any resources associated with this `ac.SceneReference` and empty it out. Use it if you need to remove a previously
---created node or a loaded KN5.
dispose = function (s)
aliveRefs[s] = nil
return ffi.C.lj_noderef_dispose__scene(s)
end,
---Set debug outline for meshes in the reference. Outline remains active until explicitly disabled or until reference is released.
---Note: each outlined group adds a render target switch and additional draw calls, so avoid adding it to more than, let’s say,
---ten groups at once (each group can have multiple meshes in it).
---@param color rgbm? @Outline color. Use `nil` or transparent color to disable outline.
---@return ac.SceneReference @Returns self for easy chaining.
setOutline = function (s, color)
ffi.C.lj_noderef_setoutline__scene(s, __util.ensure_rgbm_nil(color) or rgbm.colors.transparent)
return s
end,
---Set material property. Be careful to match the type (you need the same amount of numeric values). If you’re using boolean,-
---resulting value will be either 1 or 0.
---@param property string | "'ksEmissive'"
---@param value number|vec2|vec3|rgb|vec4|rgbm|boolean
---@return ac.SceneReference @Returns self for easy chaining.
setMaterialProperty = function (s, property, value)
property = __util.str(property)
if type(value) == 'number' then ffi.C.lj_noderef_setmaterialproperty1__scene(s, property, value)
elseif vec2.isvec2(value) then ffi.C.lj_noderef_setmaterialproperty2__scene(s, property, value)
elseif vec3.isvec3(value) then ffi.C.lj_noderef_setmaterialproperty3__scene(s, property, value)
elseif rgb.isrgb(value) then ffi.C.lj_noderef_setmaterialproperty3c__scene(s, property, value)
elseif vec4.isvec4(value) then ffi.C.lj_noderef_setmaterialproperty4__scene(s, property, value)
elseif rgbm.isrgbm(value) then ffi.C.lj_noderef_setmaterialproperty4c__scene(s, property, value)
elseif type(value) == 'boolean' then ffi.C.lj_noderef_setmaterialproperty1__scene(s, property, value and 1 or 0)
else error('Not supported type: '..value, 2) end
return s
end,
---Set material texture. Three possible uses:
---
---1. Pass color to create a new solid color texture:
--- ```
--- meshes:setMaterialTexture('txDiffuse', rgbm(1, 0, 0, 1)) -- for red color
--- ```
---2. Pass filename to load a new texture. Be careful, it would load texture syncronously unless it
--- was loaded before:
--- ```
--- meshes:setMaterialTexture('txDiffuse', 'filename.dds')
--- ```
--- Since 0.2.2 nothing will happen if the texture is missing (previously it’ll use black transparent texture).
---3. Pass a table with parameters to draw a texture in a style of scriptable displays. Be careful as to
--- not call it too often, make sure to limit refresh rate unless you really need a quick update. If you’re
--- working on a track script, might also be useful to check if camera is close enough with something like
--- ac.getSim().cameraPosition:closerToThan(display coordinates, some distance)
--- ```
--- meshes:setMaterialTexture('txDiffuse', {
--- textureSize = vec2(1024, 1024), -- although optional, I recommend to set it: skin could replace texture by one with different resolution
--- background = rgbm(1, 0, 0, 1), -- set to `nil` (or remove) to reuse original texture as background, set to `false` to skip background preparation completely
--- region = { -- if not set, whole texture will be repainted
--- from = vec2(200, 300),
--- size = vec2(400, 400)
--- },
--- callback = function (dt)
--- display.rect{ pos = …, size = …, … }
--- end
--- })
--- ```
---@param texture string | "'txDiffuse'" | "'txNormal'" | "'txEmissive'" | "'txMaps'" @Name of a texture slot.
---@tableparam value { callback: fun(dt: number), textureSize: vec2 = (512, 512), region: { from: vec2 = (0, 0), size: vec2 = (512, 512) }, background: rgbm|boolean|nil = nil }
---@overload fun(texture: string, value: string)
---@overload fun(texture: string, value: rgbm)
---@return ac.SceneReference @Returns self for easy chaining.
setMaterialTexture = function (s, texture, value)
if type(value) == 'string' or tostring(value):sub(1, 1) == '$' then
ffi.C.lj_noderef_setmaterialtexture_file__scene(s, texture, tostring(value))
elseif type(value) == 'table' then
if not value.callback then error('Callback is missing', 2) end
local dt = ffi.C.lj_noderef_setmaterialtexture_begin__scene(s, texture,
__util.ensure_vec2(value.textureSize),
value.background ~= nil and (value.background == false and _texBgSkip or __util.ensure_rgbm(value.background)) or _texBgOriginal,
__util.ensure_vec2(value.region and value.region.from), __util.ensure_vec2(value.region and value.region.size))
if dt >= 0 then
__util.pushEnsureToCall(ffi.C.lj_noderef_setmaterialtexture_end__scene)
value.callback(dt)
__util.popEnsureToCall()
end
else
ffi.C.lj_noderef_setmaterialtexture_color__scene(s, texture, __util.ensure_rgbm(value))
end
return s
end,
---Ensures all materials are unique, allowing to alter their textures and material properties without affecting the rest of the scene. Only
---ensures uniqueness relative to the rest of the scene. For example, if it refers to two meshes using the same material, they’ll continue
---to share material, but it would be their own material, separate from the scene.
---@return ac.SceneReference @Returns self for easy chaining.
ensureUniqueMaterials = function (s) ffi.C.lj_noderef_ensureuniquematerials__scene(s) return s end,
---Stores current transformation to be restored when `ac.SceneReference` is disposed (for example, when script reloads). Might be a good
---idea to use it first on any nodes you’re going to move, so all of them would get back when script is reloaded (assuming their original
---transformation is important, like it is with needles, for example).
---@return ac.SceneReference @Returns self for easy chaining.
storeCurrentTransformation = function (s) ffi.C.lj_noderef_storecurrenttransform__scene(s) return s end,
---CSP keeps track of previous world position of each node to do its motion blur. This call would clear that value, so teleported, for
---example, objects wouldn’t have motion blur artifacts for a frame.
---@return ac.SceneReference @Returns self for easy chaining.
clearMotion = function (s) ffi.C.lj_noderef_clearmotion__scene(s) return s end,
---Number of elements in this reference. Alternatively, you can use `#` operator.
---@return integer
size = function (s) return s.__size end,
---If reference is empty or not.
---@return boolean
empty = function (s) return s.__size == 0 end,
---Find any children that match filter and return a new reference to them.
---@param filter string @Node/mesh filter.
---@return ac.SceneReference @Reference to found scene elements.
findAny = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_find__scene(s, nf(filter), 0)) end,
---Find any child nodes that match filter and return a new reference to them.
---@param filter string @Node filter.
---@return ac.SceneReference @Reference to found nodes.
findNodes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_find__scene(s, nf(filter), 0x10000)) end,
---Find any child meshes that match filter and return a new reference to them.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found meshes.
findMeshes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_find__scene(s, nf(filter), 0x20000)) end,
---Find any child skinned meshes that match filter and return a new reference to them.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found skinned meshes.
findSkinnedMeshes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_find__scene(s, nf(filter), 0x40000)) end,
---Find any child objects of a certain class that match filter and return a new reference to them.
---@param objectClass ac.ObjectClass @Objects class.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found skinned meshes.
findByClass = function (s, objectClass, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_find__scene(s, nf(filter), tonumber(objectClass) or 0)) end,
---Filters current reference and returns new one with objects that match filter only.
---@param filter string @Node/mesh filter.
---@return ac.SceneReference @Reference to found scene elements.
filterAny = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_filter__scene(s, nf(filter), 0)) end,
---Filters current reference and returns new one with nodes that match filter only.
---@param filter string @Node filter.
---@return ac.SceneReference @Reference to found nodes.
filterNodes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_filter__scene(s, nf(filter), 0x10000)) end,
---Filters current reference and returns new one with meshes that match filter only.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found meshes.
filterMeshes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_filter__scene(s, nf(filter), 0x20000)) end,
---Filters current reference and returns new one with skinned meshes that match filter only.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found skinned meshes.
filterSkinnedMeshes = function (s, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_filter__scene(s, nf(filter), 0x40000)) end,
---Filters current reference and returns new one with objects of a certain class that match filter only.
---@param objectClass ac.ObjectClass @Objects class.
---@param filter string @Mesh filter.
---@return ac.SceneReference @Reference to found skinned meshes.
filterByClass = function (s, objectClass, filter) return s == nil and _emptyNodeRef() or cr(ffi.C.lj_noderef_filter__scene(s, nf(filter), tonumber(objectClass) or 0)) end,
---Create a new node with a given name and attach it as a child.
---@param name string
---@param keepAlive boolean @Set to `true` to create a long-lasting node which wouldn’t be removed when script is reloaded.
---@return ac.SceneReference @Newly created node or `nil` if failed
createNode = function (s, name, keepAlive) return cr(ffi.C.lj_noderef_createnode__scene(s, name, keepAlive ~= true), true) end,
---Create a new mesh with a given name and attach it as a child. Steals passed vertices and indices to avoid reallocating
---memory, so make sure to use `vertices:clone()` when passing if you want to keep the original data.
---@param name string
---@param materialName string?
---@param vertices ac.VertexBuffer
---@param indices ac.IndicesBuffer
---@param keepAlive boolean @Set to `true` to create a long-lasting node which wouldn’t be removed when script is reloaded.
---@param moveData boolean? @Set to `true` to move vertices and indices data thus saving on reallocating memory. You can use `vertices:clone()` for one of them to retain original array. Default value: `false`.
---@return ac.SceneReference @Newly created mesh or `nil` if failed
createMesh = function (s, name, materialName, vertices, indices, keepAlive, moveData)
local v0, v1, v2 = __util.stealVector(vertices, moveData)
local i0, i1, i2 = __util.stealVector(indices, moveData)
return cr(ffi.C.lj_noderef_createmesh__scene(s, name, materialName and tostring(materialName) or nil, keepAlive ~= true,
v0, v1, v2, i0, i1, i2), true)
end,
---Replace mesh vertices dynamically. New number of vertices should match existing one, indices work the same. Can be used for dynamic
---mesh alteration (for example, deformation). Calling it each frame with highly detailed mesh might still affect performance negatively though.
---@param vertices ac.VertexBuffer
---@return ac.SceneReference @Returns self for easy chaining.
alterVertices = function (s, vertices)
ffi.C.lj_noderef_dynamicvertices__scene(s, vertices.raw, vertices._size)
return s
end,
---Get vertices of a first mesh in selection. Makes a copy into an `ac.VertexBuffer`, so it might be expensive to call each frame, but it can be called
---once for those vertices to later be used with `:alterVertices()` method.
---@return ac.VertexBuffer? @Returns `nil` if there are no suitable meshes in selection.
getVertices = function (s)
local num = refnumber(0)
local ptr = ffi.C.lj_noderef_getvertices__scene(s, num)
return ptr ~= nil and __vecMeshVertices(num.value, ptr) or nil
end,
---Create a new bounding sphere node with a given name and attach it as a child. Using those might help with performance: children
---would skip bounding frustum test, and whole node would not get traversed during rendering if it’s not in frustum.
---
---Note: for it to work properly, it’s better to attach it to AC cars node, as that one does expect those bounding sphere nodes
---to be inside of it. You can find it with `ac.findNodes('carsRoot:yes')`.
---@param name string
---@param radius number @Radius in meters.
---@return ac.SceneReference @Can return `nil` if failed.
createBoundingSphereNode = function (s, name, radius)
return cr(ffi.C.lj_noderef_createbsnode__scene(s, name, tonumber(radius) or 1), true)
end,
---Load KN5 model and attach it as a child. To use remote models, first load them with `web.loadRemoteModel()`.
---
---Node: The way it actually works, KN5 would be loaded in a pool and then copied here (with sharing
---of resources such as vertex buffers). This generally helps with performance.
---@param filename string|{filename: string, filter: string} @KN5 filename relative to script folder or AC root folder. Since 0.2.5, you can instead pass a table with filename and a filter (for example, `'{ ! renderable:no }'`; note that filter will applied to every node and mesh).
---@return ac.SceneReference @Can return `nil` if failed.
loadKN5 = function (s, filename)
local filter = nil
if type(filename) == 'table' then
filter = filename.filter and tostring(filename.filter)
filename = filename.filename
end
return cr(ffi.C.lj_noderef_loadkn5__scene(s, filename, filter), true)
end,
---Load KN5 LOD model and attach it as a child. Parameter `mainFilename` should refer to the main KN5 with all the textures.
---
---Node: The way it actually works, KN5 would be loaded in a pool and then copied here (with sharing
---of resources such as vertex buffers). This generally helps with performance. Main KN5 would be
---loaded as well, but not shown, and instead kept in a pool.
---@param filename string|{filename: string, filter: string} @KN5 filename relative to script folder or AC root folder. Since 0.2.5, you can instead pass a table with filename and a filter (for example, `'{ ! renderable:no }'`; note that filter will applied to every node and mesh).
---@param mainFilename string @Main KN5 filename relative to script folder or AC root folder.
---@return ac.SceneReference @Can return `nil` if failed.
loadKN5LOD = function (s, filename, mainFilename)
local filter = nil
if type(filename) == 'table' then
filter = filename.filter and tostring(filename.filter)
filename = filename.filename
end
return cr(ffi.C.lj_noderef_loadkn5lod__scene(s, filename, mainFilename, filter), true)
end,
---Load KN5 model and attach it as a child asyncronously. To use remote models, first load them with `web.loadRemoteModel()`.
---
---Node: The way it actually works, KN5 would be loaded in a pool and then copied here (with sharing
---of resources such as vertex buffers). This generally helps with performance.
---@param filename string|{filename: string, filter: string} @KN5 filename relative to script folder or AC root folder. Since 0.2.5, you can instead pass a table with filename and a filter (for example, `'{ ! renderable:no }'`; note that filter will applied to every node and mesh).
---@param callback fun(err: string, loaded: ac.SceneReference?) @Callback called once model is loaded.
loadKN5Async = function (s, filename, callback)
local filter = nil
if type(filename) == 'table' then
filter = filename.filter and tostring(filename.filter)
filename = filename.filename
end
ffi.C.lj_noderef_loadkn5_async__scene(s, filename, __util.expectReply(function (err, returnIndex)
if err then callback(err, nil)
else callback(nil, cr(ffi.C.lj_noderef_access_reply__scene(returnIndex), true)) end
end), filter)
end,
---Load KN5 model and attach it as a child asyncronously. Parameter `mainFilename` should refer to the main KN5 with all the textures.
---
---Node: The way it actually works, KN5 would be loaded in a pool and then copied here (with sharing
---of resources such as vertex buffers). This generally helps with performance. Main KN5 would be
---loaded as well, but not shown, and instead kept in a pool.
---@param filename string|{filename: string, filter: string} @KN5 filename relative to script folder or AC root folder. Since 0.2.5, you can instead pass a table with filename and a filter (for example, `'{ ! renderable:no }'`; note that filter will applied to every node and mesh).
---@param mainFilename string @Main KN5 filename relative to script folder or AC root folder.
---@param callback fun(err: string, loaded: ac.SceneReference?) @Callback called once model is loaded.
loadKN5LODAsync = function (s, filename, mainFilename, callback)
local filter = nil
if type(filename) == 'table' then
filter = filename.filter and tostring(filename.filter)
filename = filename.filename
end
ffi.C.lj_noderef_loadkn5lod_async__scene(s, filename, mainFilename, __util.expectReply(function (err, returnIndex)
if err then callback(err, nil)
else callback(nil, cr(ffi.C.lj_noderef_access_reply__scene(returnIndex), true)) end
end), filter)
end,
---Loads animation from a file (on first call only), sets animation position. To use remote animations, first load them with `web.loadRemoteAnimation()`.
---@param filename string @Animation filename (”…ksanim”). If set to `nil`, no animation will be applied.
---@param position number? @Animation position from 0 to 1. Default value: 0.
---@param force boolean? @If not set to `true`, animation will be applied only if position is different from position used previously. Default value: `false`.
---@return ac.SceneReference @Returns self for easy chaining.
setAnimation = function (s, filename, position, force)
ffi.C.lj_noderef_setksanim__scene(s, filename and tostring(filename) or nil, tonumber(position) or 0, force == true)
return s
end,
---@param visible boolean
---@return ac.SceneReference @Returns self for easy chaining.
setVisible = function (s, visible) ffi.C.lj_noderef_setvisible__scene(s, visible == true) return s end,
---@param shadows boolean
---@return ac.SceneReference @Returns self for easy chaining.
setShadows = function (s, shadows) ffi.C.lj_noderef_setshadows__scene(s, shadows == true) return s end,
---@param exclude boolean
---@return ac.SceneReference @Returns self for easy chaining.
excludeFromCubemap = function (s, exclude) ffi.C.lj_noderef_setexcludecubemap__scene(s, exclude == true) return s end,
---@param exclude boolean
---@return ac.SceneReference @Returns self for easy chaining.
excludeFromSecondary = function (s, exclude) ffi.C.lj_noderef_setexcludesecondary__scene(s, exclude == true) return s end,
---@param transparent boolean
---@return ac.SceneReference @Returns self for easy chaining.
setTransparent = function (s, transparent) ffi.C.lj_noderef_settransparent__scene(s, transparent == true) return s end,
---@param mode render.BlendMode
---@return ac.SceneReference @Returns self for easy chaining.
setBlendMode = function (s, mode) ffi.C.lj_noderef_setblendmode__scene(s, tonumber(mode)) return s end,
---@param mode render.CullMode
---@return ac.SceneReference @Returns self for easy chaining.
setCullMode = function (s, mode) ffi.C.lj_noderef_setcullmode__scene(s, tonumber(mode)) return s end,
---@param mode render.DepthMode
---@return ac.SceneReference @Returns self for easy chaining.
setDepthMode = function (s, mode) ffi.C.lj_noderef_setdepthmode__scene(s, tonumber(mode)) return s end,
---Sets attribute associated with current meshes or nodes. Attributes are stored as strings, but you can access them as numbers with `:getAttibute()` by
---passing number as `defaultValue`. To find meshes with a certain attribute, use “hasAttribute:name” search query.
---@param key string
---@param value number|string|nil @Pass `nil` to remove an attribute.
---@return ac.SceneReference @Returns self for easy chaining.
setAttribute = function (s, key, value) ffi.C.lj_noderef_setattribute__scene(s, tostring(key), value ~= nil and tostring(value) or nil) return s end,
---Gets an attribute value.
---@param key string
---@param defaultValue number|string|nil @If `nil` is passed, returns string (or `nil` if attribute is not set).
---@return string|number|nil @Type is determined based on type of `defaultValue`.
getAttribute = function (s, key, defaultValue)
if type(defaultValue) == 'number' then return ffi.C.lj_noderef_getattributenum__scene(s, tostring(key), defaultValue) end
return __util.strrefp(ffi.C.lj_noderef_getattributestr__scene(s, tostring(key))) or defaultValue
end,
---Reference:
---- Reduced TAA: 1;
---- Extra TAA: 0.5.
---@param value number
---@return ac.SceneReference @Returns self for easy chaining.
setMotionStencil = function (s, value) ffi.C.lj_noderef_setmotionstencil__scene(s, tonumber(value) or 0) return s end,
---Sets position of a node (or nodes).
---
---Note: only nodes can move. If you need to move meshes, find their parent node and move it. If its parent node has more than a single mesh as a child,
---create a new node as a child of that parent and move mesh there.
---@param pos vec3
---@return ac.SceneReference @Returns self for easy chaining.
setPosition = function (s, pos) ffi.C.lj_noderef_setposition__scene(s, __util.ensure_vec3(pos)) return s end,
---Sets orientation of a node (or nodes). If vector `up` is not provided, facing up vector will be used.
---
---Note: only nodes can rotate. If you need to rotate meshes, find their parent node and rotate it. If its parent node has more than a single mesh as a child,
---create a new node as a child of that parent and move mesh there.
---@param look vec3
---@param up vec3|nil
---@return ac.SceneReference @Returns self for easy chaining.
setOrientation = function (s, look, up) ffi.C.lj_noderef_setorientation__scene(s, __util.ensure_vec3(look), up and __util.ensure_vec3(up) or _vecUp) return s end,
---Replaces orientation of a node (or nodes) with rotational matrix. If you want to just rotate node from its current orientation, use `:rotate()`.
---
---Note: only nodes can rotate. If you need to rotate meshes, find their parent node and rotate it. If its parent node has more than a single mesh as a child,
---create a new node as a child of that parent and move mesh there.
---@param axis vec3
---@param angleRad number
---@return ac.SceneReference @Returns self for easy chaining.
setRotation = function (s, axis, angleRad) ffi.C.lj_noderef_setrotation__scene(s, __util.ensure_vec3(axis), angleRad) return s end,
---Rotates node (or nodes) relative to its current orientation. If you want to completely replace its orientation by rotating matrix, use `:setRotation()`.
---
---Note: only nodes can rotate. If you need to rotate meshes, find their parent node and rotate it. If its parent node has more than a single mesh as a child,
---create a new node as a child of that parent and move mesh there.
---@param axis vec3
---@param angleRad number
---@return ac.SceneReference @Returns self for easy chaining.
rotate = function (s, axis, angleRad) ffi.C.lj_noderef_rotate__scene(s, __util.ensure_vec3(axis), angleRad) return s end,
---Returns position of a first node relative to its parent.
---@return vec3
getPosition = function (s) return ffi.C.lj_noderef_getposition__scene(s) end,
---Returns direction a first node is looking towards relative to its parent.
---@return vec3
getLook = function (s) return ffi.C.lj_noderef_getlook__scene(s) end,
---Returns direction upwards of a first node relative to its parent.
---@return vec3
getUp = function (s) return ffi.C.lj_noderef_getup__scene(s) end,
---Returns number of children of all nodes in current scene reference.
---@return integer
getChildrenCount = function (s) return ffi.C.lj_noderef_getchildrencount__scene(s) end,
---Returns reference to transformation matrix of the first node relative to its parent. If you need to move
---something often, accessing its matrix directly might be the best way. Be careful though, if there
---are no nodes in the list, it would return `nil`.
---@return mat4x4 @Reference to transformation matrix of the first node, or nil. Use `mat4x4:set()` to update its value, or access its rows directly.
getTransformationRaw = function (s)
local m = ffi.C.lj_noderef_getrawmat4x4ptr__scene(s)
return m ~= nil and m[0] or nil
end,
---Returns world transformation matrix of the first node. Do not use it to move node in world space (if you need
---to move in world space, either use `ref:getTransformationRaw():set(worldSpaceTransform:mul(ref:getParent():getWorldTransformationRaw():inverse()))` or
---simply move your node to a node without transformation, like root of dynamic objects). Be careful though, if there
---are no nodes in the list, it would return `nil`.
---@return mat4x4 @Reference to transformation matrix of the first node, or nil. Use `mat4x4:set()` to update its value, or access its rows directly.
getWorldTransformationRaw = function (s)
local m = ffi.C.lj_noderef_getrawmat4x4world__scene(s)
return m ~= nil and m[0] or nil
end,
--[[? if (ctx.flags.withPhysics) out(]]
---Sets object transformation to match transformation of a `physics.RigidBody` instance. If this object is within another object with non-identity transformation,
---it will be taken into account.
---
---There is also a method `physics.RigidBody:setTransformationFrom()` doing the opposite (and requiring an inverse of this matrix).
---@param rigidBody physics.RigidBody @Physics entity to sync transformation with.
---@param localTransform mat4x4? @Optional transformation of scene reference nodes relative to the physics entity.
---@return physics.RigidBody @Returns self for easy chaining.
setTransformationFrom = function (s, rigidBody, localTransform)
ffi.C.lj_noderef_settransformationfrom__scene(s, rigidBody, mat4x4.ismat4x4(localTransform) and localTransform or nil)
return s
end,
--[[) ?]]
---Returns AABB (minimum and maximum coordinates in vector) of static meshes in current selection. Only regular static meshes
---are taken into account (meshes created when KN5 is exported in track mode).
---@return vec3 @Minimum coordinate.
---@return vec3 @Maximum coordinate.
---@return integer @Number of static meshes in selection.
getStaticAABB = function (s)
local min, max = vec3(), vec3()
return min, max, ffi.C.lj_noderef_aabb__scene(s, min, max)
end,
---Returns AABB (minimum and maximum coordinates in vector) of meshes in current selection in local mesh coordinates. Recalculates
---AABB live, might take some time with high-poly meshes.
---@return vec3 @Minimum coordinate.
---@return vec3 @Maximum coordinate.
---@return integer @Number of static meshes in selection.
getLocalAABB = function (s)
local min, max = vec3(), vec3()
return min, max, ffi.C.lj_noderef_aabblocal__scene(s, min, max)
end,
---Returns a new scene reference with a child in certain index (assuming current scene reference points to node). If current reference
---contains several nodes, children from all of them at given index will be collected.
---@param index integer? @1-based index of a child. Default value: 1.
---@return ac.SceneReference
getChild = function (s, index) return cr(ffi.C.lj_noderef_child__scene(s, (tonumber(index) or 1) - 1)) end,
---Returns a new scene reference with first-class children (not children of children) of all nodes in current reference.
---@return ac.SceneReference
getChildren = function (s) return cr(ffi.C.lj_noderef_child__scene(s, -1)) end,
---Returns a new scene reference with a parent of an object in current scene reference. If current reference
---contains several objects, parents of all of them will be collected.
---@return ac.SceneReference
getParent = function (s) return cr(ffi.C.lj_noderef_parent__scene(s)) end,
---Adds nodes and meshes from another scene reference to current scene reference.
---@param sceneRef ac.SceneReference @Scene reference to append.
---@return ac.SceneReference @Returns self for easy chaining.
append = function (s, sceneRef) ffi.C.lj_noderef_append__scene(s, sceneRef) return s end,
---Removes nodes and meshes from another scene reference from current scene reference.
---@param sceneRef ac.SceneReference @Scene reference to remove.
---@return ac.SceneReference @Returns self for easy chaining.
subtract = function (s, sceneRef) ffi.C.lj_noderef_subtract__scene(s, sceneRef) return s end,
---Returns `true` if there is a node from `childSceneRef` somewhere in this node.
---@param childSceneRef ac.SceneReference @Scene reference to remove.
---@return boolean
contains = function (s, childSceneRef) return ffi.C.lj_noderef_contains__scene(s, childSceneRef) end,
---Clears current scene reference.
---@return ac.SceneReference @Returns self for easy chaining.
clear = function (s) ffi.C.lj_noderef_clear__scene(s) return s end,
---Casts a ray prepared by something like `render.createRay(pos, dir, length)` or `render.createMouseRay()`.
---
---If you need to access a mesh that was hit, set second argument to true:
---```
---local hitDistance, hitMesh = mesh:raycast(render.createRay(pos, dir), true)
---if hitDistance ~= -1 then
--- print(hitMesh:name())
---end
---```
---Alternatively, reuse your own scene reference for better performance if you need to cast
---a lot of rays:
---```
---local hitMesh = ac.emptySceneReference()
---local hitDistance = mesh:raycast(render.createRay(pos, dir), hitMesh)
---if hitDistance ~= -1 then
--- print(hitMesh:name())
---end
---```
---@param ray ray
---@param outSceneRef ac.SceneReference|boolean|nil
---@param outPosRef vec3|nil @Local position (not the world one).
---@param outNormalRef vec3|nil @Local normal.
---@param outUVRef vec2|nil @Texture coordinate.
---@return number @Distance to hit, or -1 if there was no hit.
---@return ac.SceneReference|nil @Reference to a mesh that was hit (same as `outSceneRef`, doubled here for convenience).
raycast = function (s, ray, outSceneRef, outPosRef, outNormalRef, outUVRef)
if outSceneRef == true then outSceneRef = _emptyNodeRef()
elseif outSceneRef == false then outSceneRef = nil end
local distance = ffi.C.lj_noderef_raycast__scene(s, outSceneRef, ray, outPosRef, outNormalRef, outUVRef)
return distance, outSceneRef
end,
---Get name of an element.
---@param index integer|nil @1-based index of an element to get a name of. Default value: 1.
---@return string @Node or mesh name.
name = function (s, index)
return __util.strrefr(ffi.C.lj_noderef_name__scene(s, (tonumber(index) or 1) - 1))
end,
---Get class of an element.
---@param index integer|nil @1-based index of an element to get a class of. Default value: 1.
---@return ac.ObjectClass @Number for class of an object.
class = function (s, index)
return ffi.C.lj_noderef_class__scene(s, (tonumber(index) or 1) - 1)
end,
---Get material name of an element.
---@param index integer|nil @1-based index of an element to get a material name of. Default value: 1.
---@return string @Material name.
materialName = function (s, index)
return __util.strrefr(ffi.C.lj_noderef_materialname__scene(s, (tonumber(index) or 1) - 1))
end,
---Get shader name of an element.
---@param index integer|nil @1-based index of an element to get a shader name of. Default value: 1.
---@return string @Shader name.
shaderName = function (s, index)
return __util.strrefr(ffi.C.lj_noderef_shadername__scene(s, (tonumber(index) or 1) - 1))
end,
---Get number of texture slots of an element.
---@param index integer|nil @1-based index of an element to get number of texture slots of. Default value: 1.
---@return integer @Number of texture slots.
getTextureSlotsCount = function (s, index)
return ffi.C.lj_noderef_textureslotcount__scene(s, (tonumber(index) or 1) - 1)
end,
---Get number of material properties of an element.
---@param index integer|nil @1-based index of an element to get number of material properties of. Default value: 1.
---@return integer @Number of material properties.
getMaterialPropertiesCount = function (s, index)
return ffi.C.lj_noderef_materialpropertycount__scene(s, (tonumber(index) or 1) - 1)
end,
---Get name of a certain texture slot of an element.
---@param index integer|nil @1-based index of an element to get a name of a certain texture slot of. Default value: 1.
---@param slotIndex integer|nil @1-based index of a texture slot. Default value: 1.
---@return string|nil @Texture slot name (like “txDiffuse” or “txNormal”) or `nil` if there is no such element or property.
getTextureSlotName = function (s, index, slotIndex)
return __util.strrefp(ffi.C.lj_noderef_textureslotname__scene(s, (tonumber(index) or 1) - 1, slotIndex and slotIndex - 1 or 0))
end,
---Get name of a certain material property of an element.
---@param index integer|nil @1-based index of an element to get a name of a certain material property of. Default value: 1.
---@param slotIndex integer|nil @1-based index of a material property. Default value: 1.
---@return string|nil @Material property name (like “ksDiffuse” or “ksAmbient”) or `nil` if there is no such element or property.
getMaterialPropertyName = function (s, index, slotIndex)
return __util.strrefp(ffi.C.lj_noderef_materialpropertyname__scene(s, (tonumber(index) or 1) - 1, slotIndex and slotIndex - 1 or 0))
end,
---Get index of a certain texture slot of an element from the name of that slot.
---@param index integer|nil @1-based index of an element to get an index of a texture slot of. Default value: 1.
---@param slotName string|"'txDiffuse'"|"'txNormal'"|"'txEmissive'"|"'txMaps'" @Name of a texture slot.
---@return integer|nil @1-based texture slot index, or `nil` if there is no such property.
---@overload fun(s: ac.SceneReference, slotName: string): integer|nil
getTextureSlotIndex = function (s, index, slotName)
if type(index) == 'string' then index, slotName = 1, index end
local r = ffi.C.lj_noderef_textureslotindex__scene(s, (tonumber(index) or 1) - 1, tostring(slotName))
return r == -1 and nil or r + 1
end,
---Get index of a certain material property of an element from the name of that property.
---@param index integer|nil @1-based index of an element to get an index of a material property of. Default value: 1.
---@param propertyName string|"'ksDiffuse'"|"'ksAmbient'"|"'ksEmissive'" @Name of material property.
---@return integer|nil @1-based material property index, or `nil` if there is no such property.
---@overload fun(s: ac.SceneReference, propertyName: string): integer|nil
getMaterialPropertyIndex = function (s, index, propertyName)
if type(index) == 'string' then index, propertyName = 1, index end
local r = ffi.C.lj_noderef_materialpropertyindex__scene(s, (tonumber(index) or 1) - 1, tostring(propertyName))
return r == -1 and nil or r + 1
end,
---Get texture filename of a certain texture slot of an element.
---@param index integer|nil @1-based index of an element to get a texture filename of. Default value: 1.
---@param slot string|integer|nil|"'txDiffuse'"|"'txNormal'"|"'txEmissive'"|"'txMaps'" @Texture slot name or a 1-based index of a texture slot. Default value: 1.
---@return string|nil @Texture filename or `nil` if there is no such slot or element.
---@overload fun(s: ac.SceneReference, slot: string): string
getTextureSlotFilename = function (s, index, slot)
if type(index) == 'string' then index, slot = 1, index end
index = (tonumber(index) or 1) - 1
slot = type(slot) == 'string' and ffi.C.lj_noderef_textureslotindex__scene(s, index, slot) or (tonumber(slot) or 1) - 1
return __util.strrefp(ffi.C.lj_noderef_textureslotfilename__scene(s, index, slot))
end,
---Dump shader replacements configs for materials in current selection. Resulting string might be pretty huge. Not all properties are dumped, but more properties might be added later. Some textures are stored as temporary IDs only valid within a session.
---@return string
dumpShaderReplacements = function (s)
return __util.strrefr(ffi.C.lj_noderef_dumpmaterials__scene(s))
end,
---@param neck ac.SceneReference
---@param modelName string
---@param carIndex integer
---@return fun(value: number): number, number
applyHumanMaterials = function (s, neck, modelName, carIndex)
ffi.C.lj_noderef_secondaryhuman__scene(s, neck, modelName, carIndex)
return function (value)
return __util.native('ac.SceneReference.setMouth', s, value)
end
end,
---Get value of a certain material property of an element.
---@param index integer|nil @1-based index of an element to get a material property of. Default value: 1.
---@param property string|integer|nil|"'ksDiffuse'"|"'ksAmbient'"|"'ksEmissive'" @Material property name or a 1-based index of a material property. Default value: 1.
---@return number|vec2|vec3|vec4|nil @Material property value (type depends on material property type), or `nil` if there is no such element or material property.
---@overload fun(s: ac.SceneReference, property: string): number|vec2|vec3|vec4|nil
getMaterialPropertyValue = function (s, index, property)
if type(index) == 'string' then index, property = 1, index end
index = (tonumber(index) or 1) - 1
property = type(property) == 'string' and ffi.C.lj_noderef_materialpropertyindex__scene(s, index, property) or (tonumber(property) or 1) - 1
local size = ffi.C.lj_noderef_materialpropertysize__scene(s, index, property)
if size == 1 then return ffi.C.lj_noderef_materialpropertyvalue1__scene(s, index, property) end
if size == 2 then return ffi.C.lj_noderef_materialpropertyvalue2__scene(s, index, property) end
if size == 3 then return ffi.C.lj_noderef_materialpropertyvalue3__scene(s, index, property) end
if size == 4 then return ffi.C.lj_noderef_materialpropertyvalue4__scene(s, index, property) end
return 0
end,
---Get number of materials in given scene reference (not recursive, only checks meshes and skinned meshes). If same material is used
---for two different meshes, it would only count once. Materials sharing same name can be different (for example, applying “[SHADER_REPLACEMENT_...]”
---via config to some meshes, not materials, forks their materials to not affect other meshes using the same material).
---@return integer @Number of materials.
getMaterialsCount = function (s)
return ffi.C.lj_noderef_materialscount__scene(s)
end,
---Creates a copy of a scene reference (not copies of nodes or meshes).
---@return ac.SceneReference
clone = function (s)
if s == nil then return _emptyNodeRef() end
return cr(ffi.C.lj_noderef_clone__scene(s), true)
end,
---Get bounding sphere of an element. Works only with meshes or skinned meshes, nodes will return nil.
---@param index integer|nil @1-based index of an element to get a bounding sphere of. Default value: 1.
---@param outVec vec3|nil @Optional vector to use for bounding sphere position, to avoid creating new vector.
---@return vec3|nil @Center of bounding sphere in parent node coordinates, or nil if there is no bounding sphere (if it’s not a mesh or a skinned mesh).
---@return number|nil @Radius of bounding sphere, or nil if there is no bounding sphere (if it’s not a mesh or a skinned mesh).
boundingSphere = function (s, index, outVec)
outVec = outVec or vec3()
local radius = ffi.C.lj_noderef_meshbs__scene(s, (tonumber(index) or 1) - 1, outVec)
if radius == -1 then return nil, nil end
return outVec, radius
end,
---Applies skin to nodes or meshes (if ran with nodes, will apply skin to all of their children meshes).
---Skin is a table storing texture names and filenames to skin textures. Example:
---```
---local skinDir = ac.getFolder(ac.FolderID.ContentCars) .. '/' .. ac.getCarID(0) .. '/skins/my_skin'
---ac.findNodes('carRoot:0'):applySkin({
--- ['metal_details.dds'] = skinDir .. '/metal_details.dds'
---})
---```
---@param skin table<string, string>
---@return ac.SceneReference @Returns self for easy chaining.
applySkin = function (s, skin) ffi.C.lj_noderef_applyskin__scene(s, __util.json(table.map(skin, function (value, key)
return tostring(value), key
end))) return s end,
---Resets textures to ones from associated KN5 file where possible.
---@return ac.SceneReference @Returns self for easy chaining.
resetSkin = function (s) ffi.C.lj_noderef_resetskin__scene(s) return s end,
---Change parent of nodes in current reference.
---@param parentSceneRef ac.SceneReference|nil @Set to nil to disconnect node from a scene.
---@return ac.SceneReference @Returns self for easy chaining.
setParent = function (s, parentSceneRef) ffi.C.lj_noderef_moveto__scene(s, parentSceneRef) return s end,
---Finds materials in another scene reference that have the same names as materials in a given scene reference,
---and copies them over, so after that both references would share materials. Example use case: if you have LOD A and
---LOD B and LOD A got unique materials (because there are multiple objects sharing same KN5 model), with this function
---it’s possible to sync together materials from LOD A and LOD B by running `lodB:setMaterialsFrom(lodA)`. And because
---materials would not be actually copied, but instead shared, any consequent change of material properly in LOD A would
---be mirrored in LOD B.
---@return ac.SceneReference @Returns self for easy chaining.
setMaterialsFrom = function (s, materialSceneRef) ffi.C.lj_noderef_setmaterialfrom__scene(s, materialSceneRef) return s end,
---Creates a new scene reference with just a single item from the original scene reference.
---Indices are 1-based. By default it would create a new scene reference, if you need to access
---a lot of objects fast, provide your own:
---```
--- local meshes = ac.findMeshes('shader:ksTree')
--- local ref = ac.emptySceneReference()
--- for i = 1, #meshes do
--- meshes:at(i, ref)
--- print(ref:name()) -- note: for this particular case, it would be more optimal to use meshes:name(i) instead
--- end
---```
---@param index integer @1-based index.
---@param outSceneRef ac.SceneReference|nil
---@return ac.SceneReference @Reference to a child, might be empty if there is no such child.
at = function (s, index, outSceneRef)
if outSceneRef == nil or outSceneRef == true then outSceneRef = _emptyNodeRef() end
ffi.C.lj_noderef_at__scene(s, outSceneRef, index - 1)
return outSceneRef
end,
---Returns number of nodes and meshes matching between this and another scene reference. Could be used to quickly find out if a certain element is in a set.
---@param other nil|ac.SceneReference|ac.SceneReference[] @Can be a single scene reference or a table with several of them.
---@return integer
countMatches = function (s, other)
if s == nil then return 0 end
other = _passRefArgs(other)
return ffi.C.lj_noderef_getintersectioncount__scene(s, other)
end,
---Creates a new scene reference containing unique elements from both sets.
---@param other nil|ac.SceneReference|ac.SceneReference[] @Can be a single scene reference or a table with several of them.
---@return ac.SceneReference
makeUnionWith = function (s, other)
if s == nil then return _emptyNodeRef() end
other = _passRefArgs(other)
return cr(ffi.C.lj_noderef_makeunion__scene(s, other))
end,
---Creates a new scene reference containing only the elements found in both of original sets.
---@param other nil|ac.SceneReference|ac.SceneReference[] @Can be a single scene reference or a table with several of them.
---@return ac.SceneReference
makeIntersectionWith = function (s, other)
if s == nil then return _emptyNodeRef() end
other = _passRefArgs(other)
return cr(ffi.C.lj_noderef_makeintersection__scene(s, other))
end,
---Creates a new scene reference containing only the elements found in first set, but not in second set.
---@param other nil|ac.SceneReference|ac.SceneReference[] @Can be a single scene reference or a table with several of them.
---@return ac.SceneReference
makeSubtractionWith = function (s, other)
if s == nil then return _emptyNodeRef() end
other = _passRefArgs(other)
return cr(ffi.C.lj_noderef_makesubtraction__scene(s, other))
end,
---Create new fake shadow node. Uses the same shading as track fake shadows.
---@tableparam params { points: vec3[], opacity: number = 1, squaredness: vec2 } @Params for newly created node.
---@return ac.SceneReference @Reference to a newly created object.
createFakeShadow = function(s, params)
if params.points and #params.points ~= 4 then error('Four points are required', 2) end
local r = cr(ffi.C.lj_noderef_createfakeshadownode__scene(s, params.name or 'FAKESHADOW'), true)
if r == nil then return nil end
if params.points ~= nil then r:setFakeShadowPoints(params.points, params.corners) end
if params.opacity ~= nil then r:setFakeShadowOpacity(params.opacity) end
if params.squaredness ~= nil then r:setFakeShadowSquaredness(params.squaredness) end
return r
end,
---Sets fake shadow points if current reference was created with `sceneReference:createFakeShadow()`.
---@param points vec3[] @Four corners.
---@param corners number[] @Four numbers for corner intensity.
---@return ac.SceneReference @Returns self for easy chaining.
setFakeShadowPoints = function(s, points, corners)
if #points ~= 4 then error('Four points are required', 2) end
ffi.C.lj_noderef_setfakeshadowpoints__scene(s,
__util.ensure_vec3(points[1]), __util.ensure_vec3(points[2]), __util.ensure_vec3(points[3]), __util.ensure_vec3(points[4]),
__util.num_or(corners and corners[1], 1), __util.num_or(corners and corners[2], 1), __util.num_or(corners and corners[3], 1), __util.num_or(corners and corners[4], 1))
return s
end,
---Sets fake shadow squaredness if current reference was created with `sceneReference:createFakeShadow()`.
---@param squaredness vec2 @X is squaredness along one axis, Y is along another.
---@return ac.SceneReference @Returns self for easy chaining.
setFakeShadowSquaredness = function(s, squaredness)
ffi.C.lj_noderef_setfakeshadowsquaredness__scene(s, __util.ensure_vec2(squaredness or vec2(1, 1)))
return s
end,
---Sets fake shadow opacity if current reference was created with `sceneReference:createFakeShadow()`.
---@param opacity number @Value from 0 to 1.
---@return ac.SceneReference @Returns self for easy chaining.
setFakeShadowOpacity = function(s, opacity)
ffi.C.lj_noderef_setfakeshadowopacity__scene(s, __util.num_or(opacity, 1))
return s
end,
---Applies shader replacements stored in INI config format. Can optionally load included files, so templates
---work as well. If there is no symbol “[” in `data`, applies passed values to all meshes and materials in selection.
---@param data string @Config in INIPP format.
---@param includeType ac.IncludeType? @Include type. If not set, includes will not be resolved, so templates won’t work. Default value: `ac.IncludeType.None`.
---@return ac.SceneReference @Returns self for easy chaining.
applyShaderReplacements = function(s, data, includeType)
ffi.C.lj_noderef_applyshaderreplacement__scene(s, tostring(data), tonumber(includeType) or 0)
return s
end,
---Projects texture onto a mesh or few meshes, draws result. Use in when updating a dynamic texture, display or an extra canvas.
---Position, and directions are set in world space.
---
---Note: this is not a regular IMGUI drawing call, so most functions, such as shading offsets, transformations or clipping, would
---not work here.
---
---Tip 1: if you want to draw a new skin for a car and apply AO to it, one way might be to draw it in a canvas and then draw
---original AO texture on top with special shading parameters:
---```
----- drawing rest of skin here
---ui.setShadingOffset(0, 0, 0, -1)
---ui.drawImage('car::EXT_body.dds', 0, ui.windowSize(), rgbm.colors.black) -- with these shading offset properties, texture
--- -- will be drawn in black with inverse of brightness used for opacity
---ui.resetShadingOffset()
---```
---
---Tip 2: if you want to project things on meshes with certain material, make sure to filter out meshes so that it would only
---affect meshes from LOD A (instead of `ac.findMeshes('material:car_paint')` use `ac.findMeshes('{ material:car_paint & lod:A}')`),
---otherwise there’d be quite a few artifacts. I spent some time like this trying to figure out why results were off.
--[[@tableparam params {
filename: string "Path to a texture, or a texture element (`ui.MediaElement`, `ui.ExtraCanvas`, `ac.GeometryShot`).",
pos: vec3 "Position from which texture will be projected, in world space.",
look: vec3 "Direction with which texture will be projected, in world space.",
up: vec3 = vec3(0, 1, 0) "Optional vector directed up, to specify texture rotation.",
color: rgbm = rgbm.colors.white "Optional color. Default value: `rgbm.colors.white`.",
colorOffset: rgbm = nil "Optional color offset. Default value: `rgbm.colors.transparent`.",
size: vec2 "Size, horizontal and vertical. Default value: `vec2(1, 1)`.",
depth: number = 1e9 "Depth: how far from camera projection goes, with a smooth falloff. Default value: 1e9.",
skew: vec2 = nil "Optional skew. Default value: `vec2(0, 0)`.",
tiling: vec2 = nil "Optional tiling for horizontal and vertical axis. With 1 tiles normally, with -1 tiles with flipping, other values are currently reserved. Default value: `vec2(0, 0)` (no tiling).",
doubleSided: boolean = false "Set to `true` to draw things on surfaces facing away as well. Default value: `false`.",
uvOffset: vec2 = nil "Optional UV offset. By default CSP estimates an UV offset such that most triagles would be shown. If mapping is way off though, it might need tweaking (or even repeated calls with different offsets).",
blendMode: render.BlendMode = nil "Optional override for blend mode. Default value: `render.BlendMode.BlendAccurate`.",
mask1: string = nil "Optional masking texture.",
mask1UV1: vec2 = nil "Optional masking texture UV coordinates.",
mask1UV2: vec2 = nil "Optional masking texture UV coordinates.",
mask1Flags: render.TextureMaskFlags = nil "Optional masking texture flags.",