Compare commits

...

337 commits

Author SHA1 Message Date
Lubos Lenco 9220e6b57e
Merge pull request #2381 from Skehmatics/fix/abc_deprication
Correct depricated (and now removed) import
2021-11-08 10:14:09 +01:00
Lubos Lenco 15ec53830f
Merge pull request #2379 from MoritzBrueckner/fix-cpu-count
Fix cpu_count() for incomplete PATH var on Windows and improve error handling
2021-11-08 10:13:36 +01:00
Lubos Lenco 946610c793
Merge pull request #2382 from Skehmatics/fix/morph_targets_linux
Fix morph target exporting on Unix-based systems
2021-11-08 09:07:13 +01:00
Moritz Brückner 1430e22a90 Make utils.cpu_count() even more failsafe on Windows 2021-11-07 16:50:20 +01:00
Derek Schmidt 07f4bea206 Fix morph target exporting on Unix-based systems
Also fixes some extra space issues since my editor auto-fixed them
2021-11-06 17:18:36 -07:00
Derek Schmidt 8581a75f9c Correct depricated (and now removed) import 2021-11-06 14:52:05 -07:00
Moritz Brückner 6bb2513001 Fix cpu_count() for incomplete PATH var on Windows and improve error handling 2021-11-06 22:36:04 +01:00
Lubos Lenco f581e91ba9 Move in morph target texture links 2021-11-06 12:34:03 +01:00
Lubos Lenco 263d5853a5
Merge pull request #2375 from QuantumCoderQC/shape-keys
Implementing Shape keys
2021-11-06 12:31:23 +01:00
QuantumCoderQC 1cec287891 Conditional compilation for shape key node 2021-11-02 15:59:40 +01:00
QuantumCoderQC 83a60e7462 Implement shape key conditions 2021-11-02 15:59:15 +01:00
QuantumCoderQC 603d23fbfd Add shape key export option 2021-11-02 15:58:38 +01:00
QuantumCoderQC 03090cafa2 remove shape key textures on clean 2021-11-02 15:56:46 +01:00
QuantumCoderQC a1951bb66e clean up export 2021-11-02 15:56:24 +01:00
QuantumCoderQC 3c0f580f69 fix morph weights lookup 2021-11-02 13:42:09 +01:00
QuantumCoderQC 8116729822 fix pixel count for images, fix max shape keys 2021-11-02 13:42:09 +01:00
QuantumCoderQC 6c0d4bdead fix error when all shape keys muted 2021-11-02 13:42:09 +01:00
QuantumCoderQC 8f733222a9 fix shape keys and multiple uv 2021-11-02 13:42:09 +01:00
QuantumCoderQC 4b65d8d68c do not export muted shape keys 2021-11-02 13:42:09 +01:00
QuantumCoderQC 13a2f4538a move shape key textures to dedicated directory 2021-11-02 13:42:09 +01:00
QuantumCoderQC b7cafbb4e3 add node to set shape key value 2021-11-02 13:42:08 +01:00
QuantumCoderQC d4f9982768 documentation 1 2021-11-02 13:42:08 +01:00
QuantumCoderQC 8e69bea14e appy position scale 2021-11-02 13:42:08 +01:00
QuantumCoderQC 669898fae1 fix normals with shape key and skin 2021-11-02 13:42:08 +01:00
QuantumCoderQC 8df6fc9176 fix shape key with skinning 2021-11-02 13:42:08 +01:00
QuantumCoderQC edeaf488b9 remove debug prints 2021-11-02 13:42:08 +01:00
QuantumCoderQC cb2cf0c9b4 keep both vert colors and shape keys 2021-11-02 13:42:07 +01:00
QuantumCoderQC 4522055fac use vec4 uniform array instead of float array 2021-11-02 13:42:07 +01:00
QuantumCoderQC 1d0eaf9371 debug prints 2021-11-02 13:42:07 +01:00
QuantumCoderQC e562d2515e export with optimized texture size 2021-11-02 13:42:07 +01:00
QuantumCoderQC 8e7d8a4f56 additional uniforms 2021-11-02 13:42:06 +01:00
QuantumCoderQC 6e153569dc make attrib skin fix 2021-11-02 13:42:06 +01:00
QuantumCoderQC d01c55fcd4 mordify to export shape keys 2021-11-02 13:42:06 +01:00
QuantumCoderQC 3814cd9da7 methods to get if vertex colors and if shapekey present 2021-11-02 13:42:06 +01:00
QuantumCoderQC 0636eb85c6 add morph attribute to shaders 2021-11-02 13:42:06 +01:00
QuantumCoderQC 2f77c5b060 modify normals calculation in skinning for morphing 2021-11-02 13:42:06 +01:00
QuantumCoderQC 26a0fb8c5a add morph target to make_shader.py 2021-11-02 13:42:05 +01:00
QuantumCoderQC 8c034655be implement morph_target.glsl 2021-11-02 13:42:05 +01:00
QuantumCoderQC 6f9b51a57a move make_morph_target 2021-11-02 13:42:05 +01:00
QuantumCoderQC 34243ed74c implement make_morph_target 2021-11-02 13:42:05 +01:00
QuantumCoderQC fb004f5dcc make attributes for shadow maps and depth 2021-11-02 13:42:05 +01:00
QuantumCoderQC 867100d151 make shader attribute for morph targets 2021-11-02 13:42:05 +01:00
QuantumCoderQC 09433f2a19 init morph target glsl 2021-11-02 13:42:05 +01:00
QuantumCoderQC 10d41e5eca init make morph target 2021-11-02 13:42:05 +01:00
QuantumCoderQC b7941d2fa8 export shape keys as texture 2021-11-02 13:42:04 +01:00
Lubos Lenco 93563dbba5 Bump version 2021-11-02 10:29:01 +01:00
Lubos Lenco 9094154440
Merge pull request #2367 from MoritzBrueckner/fix-library-textures
Fix exporting textures from linked libraries
2021-11-01 21:51:20 +01:00
Lubos Lenco 6ad9941d43
Merge pull request #2374 from tong/fix-uniformsmanager-register
Fix switch expression
2021-11-01 20:53:23 +01:00
Lubos Lenco 326d3bbf0d
Merge pull request #2373 from QuantumCoderQC/pick-rb-imp
add normal output for pick rb node
2021-11-01 20:52:23 +01:00
Lubos Lenco b2566a8249
Merge pull request #2372 from QuantumCoderQC/decal-fix
Decal fix
2021-11-01 20:51:37 +01:00
tong 18c5636d03 Fix switch expression 2021-11-01 02:44:56 +01:00
QuantumCoderQC 07cfa40727 add normal output for pick rb node 2021-10-31 23:06:11 +01:00
QuantumCoderQC e06ca4b9e7 get material from decal object too 2021-10-31 23:03:25 +01:00
QuantumCoderQC ec1f25cfec add support for decal object uniforms 2021-10-31 23:02:45 +01:00
QuantumCoderQC 1dc733ca88 dont cast decal objects to mesh objects 2021-10-31 20:58:27 +01:00
Lubos Lenco f22542a0b4
Merge pull request #2369 from t3du/master
add Between and Not Equal to Compare Logic
2021-10-28 11:40:37 +02:00
t3du c97c738870
add Between and Not Equal to Compare Logic 2021-10-27 10:35:58 -03:00
t3du 130e470290
add Between and Not Equal to Compare Logic 2021-10-27 10:32:58 -03:00
Lubos Lenco fabb4c0c3c
Merge pull request #2368 from t3du/master
add Not Equal to Gate Node
2021-10-26 16:50:04 +02:00
t3du ca03897811
Update LN_gate.py 2021-10-26 10:08:46 -03:00
t3du 6e255bcb05
add Not Equal to Gate node logic 2021-10-26 10:05:11 -03:00
t3du f6dfc43dde
add Not Equal to Gate Node 2021-10-26 10:04:31 -03:00
Moritz Brückner 36e42ad736 Fix exporting textures from linked libraries 2021-10-25 19:43:13 +02:00
Lubos Lenco 3df2916f5b
Merge pull request #2366 from MoritzBrueckner/fix-collection-export
Fix exporting collections with parent object in another collection
2021-10-25 10:00:41 +02:00
Moritz Brückner 59aca7ea41 Fix exporting collections with parent object in another collection 2021-10-23 17:12:22 +02:00
Lubos Lenco 297f7cdfca
Merge pull request #2363 from t3du/master
add get object uid nodes / agg args to call haxe static function / add Between to gate node
2021-10-22 09:18:51 +02:00
t3du 176ab43fd0
fix return with correct screen cords
solution from:

https://forums.armory3d.org/t/worldtoscreencoord-draw-debug-lines-with-2d-api/3467/9
2021-10-20 16:51:11 -03:00
t3du 6b1333902c
change version and add NodeReplacement 2021-10-20 11:15:06 -03:00
t3du 89ff1c29fc
change node version and add NodeReplacement 2021-10-20 11:12:01 -03:00
t3du 086c05eba2
change node version and add NodeReplacement 2021-10-20 11:11:44 -03:00
t3du 3e6e1e4bd1
change info 2021-10-20 09:49:00 -03:00
t3du 977b2db376
change info 2021-10-20 09:48:17 -03:00
t3du ebb3076431
add between logic condition 2021-10-19 21:19:16 -03:00
t3du 826dbd786d
add property0 between 2021-10-19 21:18:02 -03:00
t3du 4fe8f683dc
add args to the call of haxe static function 2021-10-19 19:11:10 -03:00
t3du b9b5c65141
add args to the haxe static function 2021-10-19 19:07:49 -03:00
t3du ec3054cdeb
add uid nodes 2021-10-18 17:55:44 -03:00
t3du d3b006c873
add nodes for uid 2021-10-18 17:53:09 -03:00
Lubos Lenco 9c931aff56
Merge pull request #2362 from tong/fix-doc-generation
Fix dox when using special characters
2021-10-15 16:02:15 +02:00
tong 622b8a275f Fix dox when using special characters 2021-10-14 17:35:23 +02:00
Lubos Lenco ea9b01e733
Merge pull request #2361 from SaxonGao-SZ/master
Use project name for web title
2021-10-14 12:35:03 +02:00
SaxonGao-SZ 34897c9747
feature: Use project name for web title
feature: Use project name for web title
2021-10-13 20:52:36 +08:00
Lubos Lenco c1f98dc668
Merge pull request #2359 from t3du/master
add logic nodes: set/get canvas font size and set progress bar color
2021-10-12 16:20:55 +02:00
t3du 67d511568f
add new nodes 2021-10-11 17:41:03 -03:00
t3du b8319fe28b
add new nodes 2021-10-11 17:38:18 -03:00
t3du d5e0208fd6
add getCanvasFontSize and fix setCanvasFontSize 2021-10-11 17:35:56 -03:00
Lubos Lenco 0fadefab69
Merge pull request #2357 from QuantumCoderQC/fix-random-color
random color node fix
2021-10-08 10:27:07 +02:00
QuantumCoderQC 50a1994867 random color node fix 2021-10-07 19:18:43 +02:00
Lubos Lenco b9a220fbcb
Merge pull request #2356 from MoritzBrueckner/fix-world-geometry
Fix Geometry and Texture Coordinate nodes for world shaders
2021-10-06 10:17:43 +02:00
Moritz Brückner 5e1105f51d Fix some Texture Coordinate node outputs for world shaders 2021-10-05 19:03:08 +02:00
Moritz Brückner b060953a7e Further fixes for the geometry node on world shaders 2021-10-05 18:41:45 +02:00
Moritz Brückner a4d09936d6 Fix world normals and position (adjust to Blender) 2021-10-05 18:40:39 +02:00
Lubos Lenco 786e68e475
Merge pull request #2355 from MoritzBrueckner/fix-deprecated-nodes
Fix deprecated nodes and improve deprecation handling
2021-10-05 11:02:00 +02:00
Lubos Lenco c7cba9d792
Merge pull request #2353 from MoritzBrueckner/debug-renderpath
Allow to set default renderpath for debugging
2021-10-05 11:01:00 +02:00
Lubos Lenco d9ddb5f4a3
Merge pull request #2352 from MoritzBrueckner/fix-cmft-cpu-count
Fix CPU count parameter for cmft
2021-10-05 09:39:18 +02:00
Moritz Brückner 8e8e8f586d Assert correct category for deprecated nodes 2021-10-05 00:03:48 +02:00
Moritz Brückner e8fa3b1b9a Whitespace cleanup 2021-10-04 23:57:15 +02:00
Moritz Brückner 7289b95a48 Don't show empty "Deprecated" node category menu 2021-10-04 23:56:35 +02:00
Moritz Brückner 7f5d382131 Allow to set default renderpath for debugging 2021-10-03 22:40:29 +02:00
Lubos Lenco ea02600d14 Bump version 2021-10-03 20:15:36 +02:00
Lubos Lenco a2c33b2f6a
Merge pull request #2351 from MoritzBrueckner/debug-console-trace
Debug console: make log output more readable
2021-09-28 13:00:51 +02:00
Moritz Brückner c40fd26c20 Fix CPU count parameter for cmft 2021-09-27 23:13:30 +02:00
Moritz Brückner 308ab2d77b Add some tooltips to the debug console 2021-09-27 16:02:29 +02:00
Moritz Brückner d979e4fde9 Debug console: make spacing between log lines smaller 2021-09-27 15:53:08 +02:00
Moritz Brückner 1c237dd6c3 Make printing with PosInfos configurable in the debug console UI 2021-09-27 15:46:35 +02:00
Moritz Brückner 152959699a Debug console: optionally omit PosInfos when printing 2021-09-27 15:43:03 +02:00
Moritz Brückner 259c375fda Whitespace cleanup 2021-09-27 15:41:53 +02:00
Lubos Lenco b579c8a15d
Merge pull request #2350 from MoritzBrueckner/fix-nodes-reload
Fix add-on reloading when there are new unimported node files
2021-09-27 11:03:02 +02:00
Lubos Lenco 73a352b76e
Merge pull request #2349 from MoritzBrueckner/global-canvas-scale-nodes
Add nodes to get/set global canvas scale
2021-09-27 11:02:16 +02:00
Moritz Brückner 957c5371c8 Fix add-on reloading when there are new unimported node files 2021-09-26 22:34:09 +02:00
Moritz Brückner 962f8a79a5 Add nodes to get/set global canvas scale 2021-09-26 22:31:56 +02:00
Lubos Lenco 8604e670c3
Merge pull request #2342 from tong/log-message-improvements
Improve log messages
2021-09-26 09:51:06 +02:00
tong d1d266a0f0 Improve log messages 2021-09-23 11:30:46 +02:00
Lubos Lenco 62d399e01a
Merge pull request #2338 from tong/no-haxe-times-param
Remove haxe --times param per default
2021-09-22 19:21:52 +02:00
tong 128efcc0ef Use haxe_times prefs field 2021-09-21 14:56:12 +02:00
tong 3920c475be No haxe times param 2021-09-21 14:43:47 +02:00
Lubos Lenco e3a55db813
Merge pull request #2339 from tong/fix-local-sdk-path
Fix invalid fp when using local sdk
2021-09-21 11:42:22 +02:00
Lubos Lenco 597c291da7
Merge pull request #2336 from QuantumCoderQC/tweenNodes
Introducing Tween nodes
2021-09-19 10:25:45 +02:00
Lubos Lenco 16976cdc5b
Merge pull request #2335 from MoritzBrueckner/fix-trait-fakeuser
Fix fake user export for traits
2021-09-18 11:05:19 +02:00
tong d1fe4d6686 Fix invalid fp when using local sdk 2021-09-17 13:21:36 +02:00
QuantumCoderQC ca966588ff add documentation 2021-09-13 23:10:31 +02:00
QuantumCoderQC 0ce0c5c115 change socket order 2021-09-13 22:48:32 +02:00
QuantumCoderQC 05465bcea1 minor clean-up 2021-09-13 22:30:39 +02:00
Moritz Brückner 77815d95ed Fix fake user export for traits 2021-09-13 21:29:21 +02:00
Lubos Lenco 156f1f433a
Merge pull request #2334 from tong/fix-throw-assert-error
Fix assert throw error
2021-09-13 10:03:11 +02:00
Lubos Lenco 5b4d24f067
Merge pull request #2332 from MoritzBrueckner/fix-realtime-postprocess
Fix realtime postprocess & cleanup arm.lib.make_datas
2021-09-12 08:05:29 +02:00
tong c24389813f Fix assert throw error 2021-09-12 00:03:57 +02:00
Lubos Lenco c398c1181e
Merge pull request #2331 from tong/fix-background-mode-open-build-dir
Do not open build directory in background mode
2021-09-11 13:49:20 +02:00
Lubos Lenco 7d9d5ac50f
Merge pull request #2329 from MoritzBrueckner/fix-ArmNodeAddInputOutputButton
Fix ArmNodeAddInputOutputButton
2021-09-10 09:12:59 +02:00
QuantumCoderQC eb5ef99309 create tween vector node 2021-09-10 01:08:31 +02:00
QuantumCoderQC 62294c8bb4 create tween rotation node 2021-09-10 01:08:12 +02:00
QuantumCoderQC ed6aa96fb0 create tween float node 2021-09-10 01:07:52 +02:00
Lubos Lenco f3fcb2846e
Merge pull request #2326 from onelsonic/patch-2
Update props_ui.py
2021-09-08 08:51:07 +02:00
Lubos Lenco afeecd4df1
Merge pull request #2325 from onelsonic/patch-1
Update props.py
2021-09-08 08:49:39 +02:00
Moritz Brückner de72bbf3a4 Cleanup arm.lib.make_datas 2021-09-07 23:40:52 +02:00
Moritz Brückner ecc4317919 Fix realtime postprocess: use compositor defines also for non-compositor pass shaders 2021-09-07 22:19:12 +02:00
tong 667e73c867 Do not open build directory in background mode 2021-09-07 10:15:11 +02:00
Moritz Brückner 56d84b08f4 Cleanup 2021-09-06 18:23:21 +02:00
Moritz Brückner adb2468245 Fix ArmNodeAddInputOutputButton 2021-09-06 18:22:38 +02:00
onelsonic abc52627a3
Update props_ui.py
change the UI to reflect common order to setup Android SDK values
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
android:minSdkVersion="integer"
then
android:targetSdkVersion="integer"
then
android:maxSdkVersion="integer"
2021-09-06 15:33:16 +02:00
onelsonic 68ef00b668
Update props.py
proposed UI descriptions changes to reflect current Android SDK
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
and updating default SDK values to Android Oreo
2021-09-06 15:26:13 +02:00
Lubos Lenco 55cfead084 Bump version 2021-09-06 09:22:25 +02:00
Lubos Lenco fe6d1fa372
Merge pull request #2320 from QuantumCoderQC/AddTraitFix
Add trait fix
2021-09-06 09:02:22 +02:00
Lubos Lenco 974181e75e
Merge pull request #2319 from MoritzBrueckner/assert-string-expr
Allow to use more complex string expressions as assert() messages
2021-09-05 11:55:28 +02:00
Lubos Lenco a4ab1dcc98
Merge pull request #2318 from QuantumCoderQC/navMeshImprove
Small improvement to Navigation nodes
2021-09-04 15:15:44 +02:00
QuantumCoderQC ccbc946bf1 Add warning if trait is already applied on the object 2021-09-03 23:42:43 +02:00
QuantumCoderQC fddef0983c Modify Add Trait Node To Accept String Trait 2021-09-03 23:37:13 +02:00
Moritz Brückner e715bf0108 Allow to use more complex string expressions as assert() messages 2021-09-03 22:21:57 +02:00
QuantumCoderQC 214b84fcf7 Modify NavAgent Trait 2021-09-03 21:00:58 +02:00
QuantumCoderQC 32b745f829 Modify Navigation Go To Location Node 2021-09-03 21:00:33 +02:00
QuantumCoderQC 8caf859db9 Create and implement Get Nav Agent Data Node 2021-09-03 20:59:49 +02:00
Lubos Lenco d6692efbdc
Merge pull request #2316 from onelsonic/patch-1
correcting Haxe Warnings : Std.is is deprecated. Use Std.isOfType ins…
2021-09-03 12:37:23 +02:00
onelsonic c77eea93fa
correcting Haxe Warnings : Std.is is deprecated. Use Std.isOfType instead.
correcting Warning : Std.is is deprecated. Use Std.isOfType instead.
2021-09-03 08:36:13 +02:00
Lubos Lenco e0cbf1b332
Merge pull request #2315 from MoritzBrueckner/fix-ppcomp-warnings
Fix "Uniform PPCompXX not found" warnings
2021-09-02 20:05:11 +02:00
Moritz Brückner b7024a5854 Whitespace cleanup 2021-09-02 17:18:59 +02:00
Moritz Brückner 44d8325001 Fix "Uniform PPCompXX not found" warnings 2021-09-02 17:14:18 +02:00
Lubos Lenco 052c7b00c5
Merge pull request #2309 from MoritzBrueckner/assert
Add assert() macro
2021-09-01 09:16:46 +02:00
Lubos Lenco 675618e529
Merge pull request #2308 from tong/html5-server-settings
HTML5 webserver log, port prefs
2021-08-30 09:10:26 +02:00
Moritz Brückner a6b67e4e00 Improve assert() docstring 2021-08-27 14:34:59 +02:00
Moritz Brückner 4b75a896d6 Auto-import assert() macro 2021-08-27 01:32:38 +02:00
Moritz Brückner ecddc0af92 Add assert() macro 2021-08-27 01:26:00 +02:00
tong e05d556552 Webserver log, port prefs 2021-08-26 10:01:35 +02:00
Lubos Lenco 85edde6d24
Merge pull request #2305 from niacdoial/newrotation
Added a rotation node-socket
2021-08-26 09:53:25 +02:00
niacdoial 703c618c7d
Misc. patches before merge [watch for amends]
- fixed type/import bugs in RotationNode.hx and SeparateRotationNode.hx
2021-08-21 16:07:34 +02:00
Lubos Lenco df715db381
Merge pull request #2307 from MoritzBrueckner/krom-es6
Switch to ES6 on krom target and fix 'armory' define
2021-08-20 10:35:19 +02:00
Lubos Lenco a8a44df249
Merge pull request #2306 from MoritzBrueckner/various-node-fixes
Various node fixes
2021-08-20 10:34:22 +02:00
Moritz Brückner 1eb087125f Fix 'armory' define 2021-08-19 22:27:09 +02:00
Moritz Brückner 2347348504 Switch to ES6 on krom target
See https://github.com/armory3d/armorcore/issues/32
2021-08-19 22:26:30 +02:00
Moritz Brückner 3e22cb3375 Update ArmOpenNodeWikiEntry to new wiki page structure 2021-08-19 22:02:01 +02:00
Moritz Brückner 4e526a6dd3 Various smaller node fixes 2021-08-19 21:59:52 +02:00
Moritz Brückner 2630c159d8 Align new custom sockets to Blender UI 2021-08-19 21:58:43 +02:00
niacdoial a67452802d
React to commit 14d33ee0530e5db640e0fafb8604662333c1b066 in iron. 2021-08-19 11:31:12 +02:00
niacdoial f892fdfd8a
Merge months of changes into 'newrotation' branch
(and homogeneised the contents of said branch in the process)
(plus a couple bugfixes, because what else)
2021-08-18 17:04:00 +02:00
Lubos Lenco 29785172f4
Merge pull request #2304 from tong/just-run
Shift click play just run
2021-08-18 11:03:21 +02:00
niacdoial 1d0a6d7955
Did the haxe part of the ongoing update.
Also fixed a ton of bugs in the python part.
NOTE: this requires a yet-to-be-done commit to Iron to work.
2021-08-17 19:29:35 +02:00
tong 7b6d3536e3 Shift click play just run 2021-08-17 17:47:04 +02:00
Lubos Lenco f017d68ed7
Merge pull request #2303 from MoritzBrueckner/fix-nishita-LUT
Fix nishita artifacts and improve LUT value range
2021-08-17 09:30:22 +02:00
Moritz Brückner 1de97e1898 Minor cleanup 2021-08-15 00:30:41 +02:00
Moritz Brückner ca96174b6b Fix nishita sky artifacts on some GPUs
It could happen that values returned by dot() were slightly larger than 1 or less than -1 due to precision errors, but acos() is undefined outside of [-1, 1]. This would lead to NaN values on some GPUs, causing visible artifacts.
2021-08-14 23:45:33 +02:00
niacdoial bd67667a6e Added node replacement routines for previous commits
(and fixed a couple bugs along the way)
2021-08-12 20:58:35 +02:00
Lubos Lenco 2910fcf1a8
Merge pull request #2302 from MoritzBrueckner/fix-addon-reload
Fix accidental module reloading caused by name conflicts of global vars
2021-08-11 18:25:44 +02:00
Moritz Brückner c56a0c3a72 Fix accidental module reloading caused by name conflicts of global vars 2021-08-11 14:32:21 +02:00
Moritz Brückner d610cc6a2f Fix value range in Nishita LUT 2021-08-09 16:51:26 +02:00
Lubos Lenco 34f6e0fc7c Kha update 2021-08-08 13:02:19 +02:00
Lubos Lenco 45e3c1c138
Merge pull request #2298 from MoritzBrueckner/reload-addon
Implement basic addon reloading
2021-08-07 19:06:44 +02:00
Lubos Lenco c49bece460
Merge pull request #2299 from MoritzBrueckner/fix-armsdk-ci
Fix armsdk CI
2021-08-07 19:03:57 +02:00
Lubos Lenco 2ef4f19eb7
Merge pull request #2300 from N8n5h/volumetric-light
fix volumetric lights not working when shadow map atlas is enabled
2021-08-07 16:54:08 +02:00
N8n5h 5509096158 fix volumetric lights not working with shadow map atlas enabled
Added missing code so the shader refers to the correct variable when
using shadow map atlasing.
2021-08-06 18:34:39 -03:00
Moritz Brückner 2d3e15064a Fix armsdk CI
Updated Logic.hx with changes made in make_logic.py. Is this file even used somewhere?
2021-08-06 22:33:20 +02:00
Lubos Lenco 386d2a3bf4 Bump version 2021-08-06 11:10:28 +02:00
Lubos Lenco 7607cc8ccd
Merge pull request #2296 from tong/physics-drag-limit-props
PhysicsDrag trait limit props
2021-08-05 22:13:58 +02:00
Lubos Lenco 52a594c910
Merge pull request #2297 from MoritzBrueckner/ui-font-fix
Canvas: fix using non-default fonts
2021-08-05 22:13:30 +02:00
Moritz Brückner 4c4bebcf4f Canvas: fix using non-default fonts 2021-08-04 23:35:54 +02:00
Moritz Brückner ea8c13686c Implement basic addon reloading 2021-08-04 22:56:11 +02:00
tong 4c34c2a5ce PhysicsDrag trait limit props 2021-08-04 22:09:54 +02:00
Lubos Lenco b664e3a010
Merge pull request #2287 from MoritzBrueckner/fix-thread-polling
Continue thread polling after an exception in the done callback
2021-08-02 08:23:37 +02:00
Lubos Lenco 60d0f443f8
Merge pull request #2286 from MoritzBrueckner/fix-getfp-drive-root
Windows: fix arm.utils.get_fp() if the project path is a drive root
2021-08-01 16:50:22 +02:00
Lubos Lenco aeffd76b75
Merge pull request #2285 from tong/arcball-improvements
ArcBall axis trait prop
2021-07-31 20:07:16 +02:00
Lubos Lenco 1532a332ff
Merge pull request #2284 from QuantumCoderQC/TerrainTextureFix
Fix terrain export for Krom
2021-07-30 08:25:01 +02:00
Moritz Brückner 4fc1f38b3b Continue thread polling after an exception in the done callback 2021-07-27 00:21:27 +02:00
Moritz Brückner 5f2dce140e Windows: fix arm.utils.get_fp() if the project path is a drive root 2021-07-26 23:40:47 +02:00
tong 13cf22ecc2 ArcBall axis trait prop 2021-07-26 19:26:17 +02:00
QuantumCoderQC 9f7af6a1cd Change terrain material bump output to normal 2021-07-26 18:02:23 +02:00
QuantumCoderQC 9ec425f6ae get file name only if specifiedin node 2021-07-26 13:50:52 +02:00
QuantumCoderQC 3bad878650 Add exception for null data 2021-07-26 13:48:44 +02:00
Lubos Lenco ce75516c59
Merge pull request #2283 from MoritzBrueckner/live-patch
Live patch: refactor and logic nodes support
2021-07-26 09:00:31 +02:00
Moritz Brückner bce14549ac Fix LN live patch when using multiple instances of the same logic tree 2021-07-26 00:02:54 +02:00
Moritz Brückner ee208ece18 Live patch: ignore two more operators 2021-07-25 20:20:43 +02:00
Moritz Brückner 549040fc09 Fix publishing with live patch enabled 2021-07-25 20:13:47 +02:00
Moritz Brückner 62ff11747b Update nodes for live patch support (merge conflict cleanup) 2021-07-25 19:50:51 +02:00
Moritz Brückner 6a3045477f Fix physics node if arm_physics is false + cleanup 2021-07-25 18:36:36 +02:00
Moritz Brückner 8a758bbe26 Only stop live patch session if live patch was active 2021-07-25 17:07:51 +02:00
Moritz Brückner ffee6dc521 Fix error caused by resolving merge conflict 2021-07-25 17:02:29 +02:00
Moritz Brückner cca82a69bf
Merge branch 'master' into live-patch 2021-07-25 16:47:32 +02:00
Moritz Brückner c52d25e471 arm_props.py: cleanup and fix docstrings 2021-07-25 00:15:32 +02:00
Moritz Brückner cf1dd0ac71 Live patch: fix creation of node outputs 2021-07-24 23:07:22 +02:00
Moritz Brückner 8e1aa2fb0d Live patch: fix creation of nodes without properties 2021-07-24 23:06:53 +02:00
Moritz Brückner 6d2b825dd5 Fix LN replacement and take arm_watch and arm_logic_id properties into account 2021-07-24 20:24:04 +02:00
Moritz Brückner 6050bceaf1 Fix nodes with "..._get" property names 2021-07-24 20:17:29 +02:00
Moritz Brückner dbb0764ca7 Fix typo 2021-07-24 20:16:54 +02:00
Moritz Brückner 568f4f3a6d Remove unnecessary privateAccess metadata 2021-07-24 19:51:01 +02:00
Moritz Brückner 0a58279756 Live patch: add more ignored operators 2021-07-24 19:49:35 +02:00
Moritz Brückner ff5fd9c7ed Fix generator usage 2021-07-24 19:49:19 +02:00
Moritz Brückner 0456d5f080 Remove legacy code stub 2021-07-24 18:21:06 +02:00
Moritz Brückner af520b518e Fix compatibility for frames and reroutes 2021-07-24 13:54:48 +02:00
Moritz Brückner 82c7302dd9 Live patch: simplify node deletion code 2021-07-24 13:54:31 +02:00
Moritz Brückner 2b6a7a4f78 Live patch: add another ignored operator 2021-07-24 13:37:13 +02:00
Moritz Brückner 7eced3b4f5 Live patch: don't try to re-export if live patch is not active 2021-07-24 13:36:55 +02:00
Moritz Brückner aa21402221 Live patch: handle deletion of node links 2021-07-24 13:36:16 +02:00
Lubos Lenco 97cc737aff
Merge pull request #2280 from MoritzBrueckner/fix-multi-uv
Fix usage of multiple UV maps on mobile render path
2021-07-22 09:28:00 +02:00
Moritz Brückner 256d27e289 Fix vector/color handling for logic node live patching 2021-07-21 22:58:21 +02:00
Moritz Brückner 9ff726bac1 Fix usage of multiple UV maps on mobile render path 2021-07-21 00:01:27 +02:00
Moritz Brückner 5276711094 Live patch: add more ignored operators 2021-07-20 23:34:06 +02:00
Moritz Brückner 4f13ebc439 Live patch: fix updates of object sockets 2021-07-20 23:33:47 +02:00
Moritz Brückner 1edc7a9469 Live patch: support for adding nodes 2021-07-20 20:53:37 +02:00
Moritz Brückner 3ed915b654 Cleanup LivePatch.hx 2021-07-20 14:28:42 +02:00
Moritz Brückner e930da7388 Live patch: support for node deletion 2021-07-20 14:25:35 +02:00
Moritz Brückner c65764be99 Cleanup LivePatch.hx 2021-07-20 14:24:00 +02:00
Moritz Brückner bdee03873b Live patch: support for node duplication 2021-07-18 22:29:18 +02:00
Lubos Lenco 1b758ec32c
Merge pull request #2276 from tong/fix-fetch-prop-type
Fix fetch trait prop type
2021-07-18 09:12:44 +02:00
tong cfa941eab4 Fix fetch trait prop type 2021-07-17 18:49:37 +02:00
niacdoial 9b1393ea41
Make rotation its own socket in logic nodes, add rotation-specific math node
(part 1: conversion code not developed)
2021-07-11 11:28:00 +02:00
Lubos Lenco bfde181da6
Merge pull request #2268 from MoritzBrueckner/fix-gbuffer2-overlay
Fix visual artifacts caused by invalid gbuffer2 on OpenGL
2021-07-11 10:48:00 +02:00
Lubos Lenco a59c789528
Merge pull request #2266 from QuantumCoderQC/staticTargetFix
Static target fix
2021-07-11 10:46:52 +02:00
Lubos Lenco caa03b59c5
Merge pull request #2263 from knowledgenude/master
Fix nullability
2021-07-11 10:42:12 +02:00
Lubos Lenco ff93e5f824
Merge pull request #2262 from tong/replace-deprecated-std-is
Replace deprecated Std.is with Std.isOfType
2021-07-11 10:41:53 +02:00
Moritz Brückner b3162d8f6e Fix visual artifacts caused by invalid gbuffer2 on OpenGL 2021-07-10 22:41:19 +02:00
Moritz Brückner 823cc379b6 Live patch: ignore more operators & fix code style 2021-07-10 22:08:19 +02:00
Moritz Brückner ee194a1806 Live patch: support for socket default values 2021-07-10 21:50:30 +02:00
Moritz Brückner 96aa0ee890 Use custom sockets for default data types
This allows to listen for socket updates for the live patch system
2021-07-10 21:46:44 +02:00
QuantumCoderQC c0333db44f Fix typos. Fix fast float 2021-07-10 19:22:26 +02:00
QuantumCoderQC e0ff256f40 make boolean objects nullable for static targets 2021-07-10 19:20:56 +02:00
Henrique 28d21bf35f Fix nullability 2021-07-09 21:42:12 -03:00
tong b452acaebb Replace deprecated Std.is with Std.isOfType 2021-07-09 22:04:50 +02:00
Lubos Lenco 3c88ddcb23
Merge pull request #2260 from knowledgenude/master
Improved Input Map and added nodes to it
2021-07-09 07:52:24 +02:00
Henrique db5aed465e Add static method to remove keys 2021-07-08 16:10:45 -03:00
Henrique 55cf2896a2 Cleanup 2021-07-07 17:11:56 -03:00
Henrique daee309ad8 Add Remove Input Map Key node 2021-07-07 17:08:01 -03:00
Henrique 43a574eb15 Remove unnecessary read access 2021-07-07 16:51:07 -03:00
Henrique 919512fad0 Improve InputMap and add nodes to it 2021-07-07 16:33:20 -03:00
Lubos Lenco 14b18408aa
Merge pull request #2254 from N8n5h/fix-atlas-ui
Fix compiling error with shadow map atlas shadow size option
2021-07-06 23:35:35 +02:00
N8n5h 801668a0c2 Fix compiling error with shadow map atlas shadow size option
Added 512 option to Inc so it doesn't fails compilation because of
missing option as explained here
https://github.com/armory3d/armory/issues/2252#issue-937328497
2021-07-06 10:39:07 -03:00
Lubos Lenco dc6753c2ca Bump version 2021-07-06 10:46:44 +02:00
Lubos Lenco 6dd27bfeed
Merge pull request #2251 from MoritzBrueckner/ui-canvas-font
UI Canvas: use font from asset for all element types
2021-07-06 09:20:43 +02:00
Moritz Brückner e7da337530 UI Canvas: use font from asset for all element types 2021-07-05 19:25:47 +02:00
Lubos Lenco 50c8ceec1e
Merge pull request #2247 from QuantumCoderQC/LogicNodeFix
Add check to LogicNode run
2021-07-04 23:19:57 +02:00
Moritz Brückner a6ec652d5f Fix identity node replacement for Blender 2.93 2021-07-03 22:49:19 +02:00
QuantumCoderQC cb800729d2 Add check
Add a check to see if input socket is linked to correct output socket
2021-07-03 20:12:10 +02:00
Moritz Brückner 4e19ddfeb0 Do not send live patch events if live patch isn't running 2021-07-03 19:47:09 +02:00
Moritz Brückner b2153dbcd2 Cleanup 2021-07-03 19:45:35 +02:00
Moritz Brückner 4387d774cc Live patch: add support for node property updates 2021-07-03 19:45:05 +02:00
Lubos Lenco 738a217001
Merge pull request #2245 from QuantumCoderQC/NavMeshNodeFix
A few small fixes to UniformsManager calss and go to location node
2021-07-02 19:24:34 +02:00
QuantumCoderQC 9e493f6e79 do not remove trait at init 2021-07-01 15:33:28 +02:00
QuantumCoderQC 7504d5d92b Change input type from shader to vector 2021-07-01 15:32:53 +02:00
Lubos Lenco e308d9058b
Merge pull request #2242 from knowledgenude/master
Add "Parent Relative" option to location nodes
2021-06-29 08:44:17 +02:00
Henrique c7e1f5d0a9 Fix nodes 2021-06-28 16:12:05 -03:00
Moritz Brückner 202138304a Live patch: add support for creating connections between nodes 2021-06-28 12:17:13 +02:00
Lubos Lenco 8c0b6ba13e
Merge pull request #2240 from QuantumCoderQC/RelativePhysConstraint
Add option for relative physics constraint
2021-06-28 11:44:39 +02:00
Henrique 7515a20d93 Keep compatibility 2021-06-27 20:47:55 -03:00
Henrique 3d49edee71 Add Subtract option to World Vector to Local Space node 2021-06-27 19:13:00 -03:00
Henrique 34b816b4d9 Remove default value 2021-06-27 18:27:16 -03:00
Henrique 13c1e0508d Fix for null parent 2021-06-27 18:26:13 -03:00
Henrique f940e8566e Remove unused quat 2021-06-27 18:21:59 -03:00
Henrique dfad6902af Add Relative Parent option to location nodes 2021-06-27 18:18:04 -03:00
Moritz Brückner 441f42383e Correctly stop live patching after player terminates 2021-06-27 22:35:37 +02:00
Lubos Lenco 616a0e230d
Merge pull request #2239 from QuantumCoderQC/MatParamPerObject
Set material parameter on per object basis
2021-06-26 12:17:40 +02:00
Lubos Lenco 20540ccb11
Merge pull request #2238 from MoritzBrueckner/background-mode
Fix threading and publishing in background mode
2021-06-25 08:57:16 +02:00
QuantumCoderQC 31a2c9c4d3 Modify export of physics constraint 2021-06-24 23:13:29 +02:00
QuantumCoderQC ac48fd0bc9 Implement adding of relative physics constraint 2021-06-24 23:12:51 +02:00
QuantumCoderQC f2cf3bdeda Add new property to set if constraint is relative 2021-06-24 23:12:15 +02:00
QuantumCoderQC 112c00a649 Modify unifroms manager 2021-06-24 21:33:53 +02:00
QuantumCoderQC ee43724b97 Change imports 2021-06-24 21:33:21 +02:00
QuantumCoderQC 5a2f952f89 Add uniforms manager to mesh objects 2021-06-24 21:32:48 +02:00
QuantumCoderQC 936f11ed8e Move uniforms manager back to armory 2021-06-24 18:19:33 +02:00
QuantumCoderQC eab7fcbbe6 update node implementations 2021-06-24 18:13:13 +02:00
QuantumCoderQC 5a591fa15a set default vector and float parameter 2021-06-24 18:13:13 +02:00
QuantumCoderQC 975ec76f38 Set default image file 2021-06-24 18:13:13 +02:00
QuantumCoderQC 340f7e8af4 Pass default image file name when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC fa44147ee6 Pass default float value when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC c44e2cf555 Pass default vector value when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC 15da6ccf58 Upgrade nodes to include object. Add documnetation 2021-06-24 18:13:12 +02:00
QuantumCoderQC 39922bc0f3 move uniforms manager to iron 2021-06-24 18:13:12 +02:00
QuantumCoderQC ea4f88aca8 remove debug trace 2021-06-24 18:13:12 +02:00
QuantumCoderQC 39bd32f9d3 get image from node 2021-06-24 18:13:12 +02:00
QuantumCoderQC e744fd901c create and implement uniforms manager
remove node mapping

Revert "remove node mapping"

This reverts commit e70aa60e120e71236cba885bd7e0e5f1b6acf39d.
2021-06-24 18:13:12 +02:00
QuantumCoderQC dbd348ad5d update node scripts 2021-06-24 18:13:12 +02:00
QuantumCoderQC f542dc00ca update similar nodes 2021-06-24 18:13:12 +02:00
QuantumCoderQC aaa21bc019 implement per object option 2021-06-24 18:13:12 +02:00
QuantumCoderQC a9f430c374 Add per object option 2021-06-24 18:13:12 +02:00
QuantumCoderQC 6cf3299ffe Implement and upgrade set material value parameter node 2021-06-24 18:13:12 +02:00
QuantumCoderQC a588396dea Update set material value param node 2021-06-24 18:13:12 +02:00
Moritz Brückner dc34e48c52 Unregister timers on exit/disabling addon 2021-06-24 17:26:21 +02:00
Moritz Brückner fce97a5ddf Cleanup 2021-06-24 15:03:30 +02:00
Moritz Brückner d18aede964 run_proc: no thread in background mode and call done in main thread 2021-06-24 14:59:32 +02:00
Moritz Brückner 66856e7ecc Build/publish: add poll() function to prevent exception when executing from outside of UI
If there was no exporter, calling `bpy.ops.arm.publish_project()` would result in an exception before. Now the call is simply ignored and a "poll failed" message is emitted instead.
2021-06-23 20:19:08 +02:00
Lubos Lenco ebfbb68bb4
Merge pull request #2235 from knowledgenude/master
Cleanup in ui Ext
2021-06-20 20:53:48 +02:00
Henrique 3c1264378b Cleanup in ui Ext 2021-06-19 13:39:18 -03:00
Lubos Lenco cbcc3e4e04
Merge pull request #2233 from MoritzBrueckner/fix-compilation
Fix compilation of some nodes
2021-06-16 08:10:03 +02:00
Moritz Brückner f45304ea10 Fix compilation of some nodes 2021-06-15 15:24:58 +02:00
Lubos Lenco dd13cc31b7
Merge pull request #2231 from knowledgenude/master
Fix GetBoneFkIkOnly node & add new input nodes
2021-06-14 10:41:51 +02:00
Henrique a5fa3445d9 Add new input nodes 2021-06-13 22:43:13 -03:00
Henrique 1ece052aee Fix GetBoneFkIkOnly node 2021-06-13 21:38:15 -03:00
Lubos Lenco 24d917ae6a
Merge pull request #2228 from MoritzBrueckner/fix-movie-texture
Fix rendering multiple movie textures
2021-06-10 21:12:32 +02:00
Moritz Brückner 3288c3dcf5 MovieTexture: add documentation 2021-06-10 20:28:30 +02:00
Moritz Brückner 251ad8e47e Cache and reuse movietexture render targets with same size 2021-06-10 20:27:23 +02:00
Moritz Brückner b2619828eb Fix rendering multiple movie textures
Fixes https://github.com/armory3d/armory/issues/1562
2021-06-09 23:11:22 +02:00
Lubos Lenco 6c6d7c1419
Merge pull request #2221 from QuantumCoderQC/ExtraLogicNodes
Extra logic nodes
2021-06-09 07:38:28 +02:00
QuantumCoderQC 0e6ba5b0b0 reverse call order 2021-06-07 13:49:12 +02:00
QuantumCoderQC 107dc730f4 Create and implement once per frame node 2021-06-07 13:41:24 +02:00
Lubos Lenco b72e85894a
Merge pull request #2220 from MoritzBrueckner/select-node
Add Select node
2021-06-07 11:03:42 +02:00
Moritz Brückner 20f9f8a5f4 Add Select node
Implements feature requests #2200 and #2201
2021-06-05 20:35:21 +02:00
Moritz Brückner 088bc0f666 Exporter: remove no longer used Blender version checks 2021-05-20 20:38:43 +02:00
Moritz Brückner 9fbc3d6cd4 Small cleanup 2021-05-19 21:17:57 +02:00
Moritz Brückner 7b2961459c Live patch support for light strength and color 2021-05-19 21:17:38 +02:00
Moritz Brückner 3d46910530 Live patch: don't re-export on trackball rotation (double 'r' key) 2021-05-18 21:34:46 +02:00
Moritz Brückner 68825516c9 Live patch: more robust shader paths 2021-05-18 21:31:02 +02:00
Moritz Brückner 07ffe06c1d Live patch: move to dedicated module and use msgbus 2021-05-18 21:24:45 +02:00
524 changed files with 9297 additions and 3531 deletions

View file

@ -4,7 +4,9 @@
uniform sampler2D tex; uniform sampler2D tex;
#ifdef _CPostprocess
uniform vec3 PPComp10; uniform vec3 PPComp10;
#endif
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor; out vec4 fragColor;

View file

@ -8,7 +8,8 @@
"links": [ "links": [
{ {
"name": "PPComp10", "name": "PPComp10",
"link": "_PPComp10" "link": "_PPComp10",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],

View file

@ -7,7 +7,9 @@ uniform sampler2D tex;
uniform vec2 dir; uniform vec2 dir;
uniform vec2 screenSize; uniform vec2 screenSize;
#ifdef _CPostprocess
uniform vec3 PPComp11; uniform vec3 PPComp11;
#endif
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor; out vec4 fragColor;
@ -26,7 +28,7 @@ void main() {
fragColor.rgb += textureLod(tex, texCoord + s, 0.0).rgb * weight[i]; fragColor.rgb += textureLod(tex, texCoord + s, 0.0).rgb * weight[i];
fragColor.rgb += textureLod(tex, texCoord - s, 0.0).rgb * weight[i]; fragColor.rgb += textureLod(tex, texCoord - s, 0.0).rgb * weight[i];
} }
#ifdef _CPostprocess #ifdef _CPostprocess
fragColor.rgb *= PPComp11.x / 5; fragColor.rgb *= PPComp11.x / 5;
#else #else

View file

@ -16,7 +16,8 @@
}, },
{ {
"name": "PPComp11", "name": "PPComp11",
"link": "_PPComp11" "link": "_PPComp11",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],
@ -39,7 +40,8 @@
}, },
{ {
"name": "PPComp11", "name": "PPComp11",
"link": "_PPComp11" "link": "_PPComp11",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],
@ -65,7 +67,8 @@
}, },
{ {
"name": "PPComp11", "name": "PPComp11",
"link": "_PPComp11" "link": "_PPComp11",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],

View file

@ -3,7 +3,10 @@
#include "compiled.inc" #include "compiled.inc"
uniform sampler2D tex; uniform sampler2D tex;
#ifdef _CPostprocess
uniform vec3 PPComp13; uniform vec3 PPComp13;
#endif
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor; out vec4 fragColor;

View file

@ -9,7 +9,8 @@
"links": [ "links": [
{ {
"name": "PPComp13", "name": "PPComp13",
"link": "_PPComp13" "link": "_PPComp13",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],

View file

@ -21,7 +21,9 @@
uniform sampler2D gbufferD; uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1; uniform sampler2D gbuffer1;
#ifdef _gbuffer2
uniform sampler2D gbuffer2; uniform sampler2D gbuffer2;
#endif
#ifdef _VoxelAOvar #ifdef _VoxelAOvar
uniform sampler3D voxels; uniform sampler3D voxels;
@ -206,7 +208,9 @@ void main() {
vec3 v = normalize(eye - p); vec3 v = normalize(eye - p);
float dotNV = max(dot(n, v), 0.0); float dotNV = max(dot(n, v), 0.0);
#ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
#endif
#ifdef _MicroShadowing #ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
@ -221,14 +225,16 @@ void main() {
vec3 envl = shIrradiance(n, shirr); vec3 envl = shIrradiance(n, shirr);
if (g2.b < 0.5) { #ifdef _gbuffer2
envl = envl; if (g2.b < 0.5) {
} else { envl = envl;
envl = vec3(1.0); } else {
} envl = vec3(1.0);
}
#endif
#ifdef _EnvTex #ifdef _EnvTex
envl /= PI; envl /= PI;
#endif #endif
#else #else
vec3 envl = vec3(1.0); vec3 envl = vec3(1.0);

View file

@ -12,8 +12,10 @@ uniform vec3 eyeLook;
uniform vec2 screenSize; uniform vec2 screenSize;
uniform mat4 invVP; uniform mat4 invVP;
#ifdef _CPostprocess
uniform vec3 PPComp11; uniform vec3 PPComp11;
uniform vec3 PPComp12; uniform vec3 PPComp12;
#endif
in vec2 texCoord; in vec2 texCoord;
in vec3 viewRay; in vec3 viewRay;
@ -23,12 +25,12 @@ void main() {
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
if (depth == 1.0) { fragColor = 1.0; return; } if (depth == 1.0) { fragColor = 1.0; return; }
vec2 enc = textureLod(gbuffer0, texCoord, 0.0).rg; vec2 enc = textureLod(gbuffer0, texCoord, 0.0).rg;
vec3 n; vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y); n.z = 1.0 - abs(enc.x) - abs(enc.y);
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
n = normalize(n); n = normalize(n);
vec3 vray = normalize(viewRay); vec3 vray = normalize(viewRay);
vec3 currentPos = getPosNoEye(eyeLook, vray, depth, cameraProj); vec3 currentPos = getPosNoEye(eyeLook, vray, depth, cameraProj);
// vec3 currentPos = getPos2NoEye(eye, invVP, depth, texCoord); // vec3 currentPos = getPos2NoEye(eye, invVP, depth, texCoord);
@ -53,7 +55,7 @@ void main() {
vec3 pos = getPos2NoEye(eye, invVP, depth, texCoord + k) - currentPos; vec3 pos = getPos2NoEye(eye, invVP, depth, texCoord + k) - currentPos;
fragColor += max(0, dot(pos, n) - currentDistanceB) / (dot(pos, pos) + 0.015); fragColor += max(0, dot(pos, n) - currentDistanceB) / (dot(pos, pos) + 0.015);
} }
#ifdef _CPostprocess #ifdef _CPostprocess
fragColor *= (PPComp12.x * 0.3) / samples; fragColor *= (PPComp12.x * 0.3) / samples;
#else #else

View file

@ -28,11 +28,13 @@
}, },
{ {
"name": "PPComp11", "name": "PPComp11",
"link": "_PPComp11" "link": "_PPComp11",
"ifdef": ["_CPostprocess"]
}, },
{ {
"name": "PPComp12", "name": "PPComp12",
"link": "_PPComp12" "link": "_PPComp12",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],

View file

@ -12,8 +12,10 @@ uniform mat4 P;
uniform mat3 V3; uniform mat3 V3;
uniform vec2 cameraProj; uniform vec2 cameraProj;
#ifdef _CPostprocess
uniform vec3 PPComp9; uniform vec3 PPComp9;
uniform vec3 PPComp10; uniform vec3 PPComp10;
#endif
in vec3 viewRay; in vec3 viewRay;
in vec2 texCoord; in vec2 texCoord;

View file

@ -24,11 +24,13 @@
}, },
{ {
"name": "PPComp9", "name": "PPComp9",
"link": "_PPComp9" "link": "_PPComp9",
"ifdef": ["_CPostprocess"]
}, },
{ {
"name": "PPComp10", "name": "PPComp10",
"link": "_PPComp10" "link": "_PPComp10",
"ifdef": ["_CPostprocess"]
} }
], ],
"texture_params": [], "texture_params": [],

View file

@ -23,7 +23,7 @@ vec2 rand2(const vec2 coord) {
const float width = 1100.0; const float width = 1100.0;
const float height = 500.0; const float height = 500.0;
float noiseX = ((fract(1.0 - coord.s * (width / 2.0)) * 0.25) + (fract(coord.t * (height / 2.0)) * 0.75)) * 2.0 - 1.0; float noiseX = ((fract(1.0 - coord.s * (width / 2.0)) * 0.25) + (fract(coord.t * (height / 2.0)) * 0.75)) * 2.0 - 1.0;
float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0; float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0;
return vec2(noiseX, noiseY); return vec2(noiseX, noiseY);
} }
@ -40,4 +40,9 @@ float attenuate(const float dist) {
// 1.0 / (quadratic * dist * dist); // 1.0 / (quadratic * dist * dist);
} }
float safe_acos(const float x) {
// acos is undefined if |x| > 1
return acos(clamp(x, -1.0, 1.0));
}
#endif #endif

View file

@ -0,0 +1,53 @@
uniform sampler2D morphDataPos;
uniform sampler2D morphDataNor;
uniform vec2 morphScaleOffset;
uniform vec2 morphDataDim;
uniform vec4 morphWeights[8];
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].x * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].y * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].z * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].w * morph;
}
}
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
morphNor += norm;
}
morphNor = normalize(morphNor);
}

View file

@ -20,6 +20,8 @@
#ifndef _SKY_GLSL_ #ifndef _SKY_GLSL_
#define _SKY_GLSL_ #define _SKY_GLSL_
#include "std/math.glsl"
uniform sampler2D nishitaLUT; uniform sampler2D nishitaLUT;
uniform vec2 nishitaDensity; uniform vec2 nishitaDensity;
@ -44,20 +46,9 @@ uniform vec2 nishitaDensity;
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction") #define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy #define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
// The ozone absorption coefficients are taken from Cycles code.
// Because Cycles calculates 21 wavelengths, we use the coefficients
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
// Precalculating values by simulating Blender's spec_to_xyz() function
// to include all 21 wavelengths gave unrealistic results
#define nishita_ozone_coeff vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914)
// Values from [Hill: 60] // Values from [Hill: 60]
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652) #define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
float random(vec2 coords) {
return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453);
}
vec3 nishita_lookupLUT(const float height, const float sunTheta) { vec3 nishita_lookupLUT(const float height, const float sunTheta) {
vec2 coords = vec2( vec2 coords = vec2(
sqrt(height * (1 / nishita_atmo_radius)), sqrt(height * (1 / nishita_atmo_radius)),
@ -124,18 +115,19 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the // Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith). // inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
float sunTheta = acos(dot(normalize(iPos), normalize(pSun))); float sunTheta = safe_acos(dot(normalize(iPos), normalize(pSun)));
vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta); vec3 jAttn = nishita_lookupLUT(iHeight, sunTheta);
// Apply dithering to reduce visible banding
jODepth += mix(-1000, 1000, random(r.xy));
// Calculate attenuation // Calculate attenuation
vec3 attn = exp(-( vec3 iAttn = exp(-(
nishita_mie_coeff * (iOdMie + jODepth.y) nishita_mie_coeff * iOdMie
+ (nishita_rayleigh_coeff) * (iOdRlh + jODepth.x) + nishita_rayleigh_coeff * iOdRlh
+ nishita_ozone_coeff * jODepth.z // + 0 for ozone
)); ));
vec3 attn = iAttn * jAttn;
// Apply dithering to reduce visible banding
attn *= 0.98 + rand(r.xy) * 0.04;
// Accumulate scattering // Accumulate scattering
totalRlh += odStepRlh * attn; totalRlh += odStepRlh * attn;

View file

@ -44,7 +44,15 @@ uniform vec2 cameraPlane;
uniform vec3 sunDir; uniform vec3 sunDir;
uniform vec3 sunCol; uniform vec3 sunCol;
#ifdef _ShadowMap #ifdef _ShadowMap
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
#else
uniform sampler2DShadow shadowMapAtlas;
#endif
#else
uniform sampler2DShadow shadowMap; uniform sampler2DShadow shadowMap;
#endif
uniform float shadowsBias; uniform float shadowsBias;
#ifdef _CSM #ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4]; //!uniform vec4 casData[shadowmapCascades * 4 + 4];
@ -95,7 +103,17 @@ void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatter
#endif #endif
vec4 lPos = LWVP * vec4(curPos, 1.0); vec4 lPos = LWVP * vec4(curPos, 1.0);
lPos.xyz /= lPos.w; lPos.xyz /= lPos.w;
visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)); visibility = texture(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, vec3(lPos.xy, lPos.z - shadowsBias));
#endif #endif
#ifdef _SinglePoint #ifdef _SinglePoint

1
Sources/armory/import.hx Normal file
View file

@ -0,0 +1 @@
import armory.system.Assert.*;

View file

@ -1,13 +1,11 @@
package armory.logicnode; package armory.logicnode;
import armory.trait.physics.PhysicsConstraint;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintType;
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object; import iron.object.Object;
import armory.trait.physics.RigidBody;
import armory.logicnode.PhysicsConstraintNode; #if arm_physics
import armory.trait.physics.PhysicsConstraint;
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintType;
#end
class AddPhysicsConstraintNode extends LogicNode { class AddPhysicsConstraintNode extends LogicNode {
@ -21,115 +19,96 @@ class AddPhysicsConstraintNode extends LogicNode {
} }
override function run(from: Int) { override function run(from: Int) {
var pivotObject:Object = inputs[1].get(); var pivotObject: Object = inputs[1].get();
rb1 = inputs[2].get(); rb1 = inputs[2].get();
rb2 = inputs[3].get(); rb2 = inputs[3].get();
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
var breakingThreshold: Float = inputs[6].get();
var type: ConstraintType = 0;
if (pivotObject == null || rb1 == null || rb2 == null) return; if (pivotObject == null || rb1 == null || rb2 == null) return;
#if arm_physics #if arm_physics
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
var breakingThreshold: Float = inputs[6].get();
var type: ConstraintType = 0;
var con: PhysicsConstraint = pivotObject.getTrait(PhysicsConstraint); var con: PhysicsConstraint = pivotObject.getTrait(PhysicsConstraint);
if(con == null) if (con == null) {
{ switch (property0) {
switch(property0) case "Fixed": type = Fixed;
{ case "Point": type = Point;
case 'Fixed': case "Hinge": type = Hinge;
type = Fixed; case "Slider": type = Slider;
case 'Point': case "Piston": type = Piston;
type = Point; case "Generic Spring": type = Generic;
case 'Hinge':
type = Hinge;
case 'Slider':
type = Slider;
case 'Piston':
type = Piston;
case 'Generic Spring':
type = Generic;
} }
if(! breakable) breakingThreshold = 0.0; if (!breakable) breakingThreshold = 0.0;
if (type != Generic) {
if(type != Generic) {
con = new PhysicsConstraint(rb1, rb2, type, disableCollisions, breakingThreshold); con = new PhysicsConstraint(rb1, rb2, type, disableCollisions, breakingThreshold);
switch (type) switch (type) {
{
case Hinge: case Hinge:
var setLimit:Bool = inputs[7].get(); var setLimit: Bool = inputs[7].get();
var low:Float = inputs[8].get(); var low: Float = inputs[8].get();
var up:Float = inputs[9].get(); var up: Float = inputs[9].get();
con.setHingeConstraintLimits(setLimit, low, up); con.setHingeConstraintLimits(setLimit, low, up);
case Slider: case Slider:
var setLimit:Bool = inputs[7].get(); var setLimit: Bool = inputs[7].get();
var low:Float = inputs[8].get(); var low: Float = inputs[8].get();
var up:Float = inputs[9].get(); var up: Float = inputs[9].get();
con.setSliderConstraintLimits(setLimit, low, up); con.setSliderConstraintLimits(setLimit, low, up);
case Piston: case Piston:
var setLinLimit:Bool = inputs[7].get(); var setLinLimit: Bool = inputs[7].get();
var linLow:Float = inputs[8].get(); var linLow: Float = inputs[8].get();
var linUp:Float = inputs[9].get(); var linUp: Float = inputs[9].get();
var setAngLimit:Bool = inputs[10].get(); var setAngLimit: Bool = inputs[10].get();
var angLow:Float = inputs[11].get(); var angLow: Float = inputs[11].get();
var angUp:Float = inputs[12].get(); var angUp: Float = inputs[12].get();
con.setPistonConstraintLimits(setLinLimit, linLow, linUp, setAngLimit, angLow, angUp); con.setPistonConstraintLimits(setLinLimit, linLow, linUp, setAngLimit, angLow, angUp);
default: default:
} }
} }
else else {
{
var spring: Bool = false; var spring: Bool = false;
var prop: PhysicsConstraintNode; var prop: PhysicsConstraintNode;
for(inp in 7...inputs.length)
{ for (inp in 7...inputs.length) {
prop = inputs[inp].get(); prop = inputs[inp].get();
if(prop == null) continue; if (prop == null) continue;
if(prop.isSpring) if (prop.isSpring) {
{
spring = true; spring = true;
break; break;
} }
} }
if(spring) { if (spring) {
con = new PhysicsConstraint(rb1, rb2, GenericSpring, disableCollisions, breakingThreshold); con = new PhysicsConstraint(rb1, rb2, GenericSpring, disableCollisions, breakingThreshold);
} }
else { else {
con = new PhysicsConstraint(rb1, rb2, Generic, disableCollisions, breakingThreshold); con = new PhysicsConstraint(rb1, rb2, Generic, disableCollisions, breakingThreshold);
} }
for(inp in 7...inputs.length) for (inp in 7...inputs.length) {
{
prop = inputs[inp].get(); prop = inputs[inp].get();
if(prop == null) continue; if (prop == null) continue;
(inp + ': ');
if(prop.isSpring) if (prop.isSpring) {
{
con.setSpringParams(prop.isSpring, prop.value1, prop.value2, prop.axis, prop.isAngular); con.setSpringParams(prop.isSpring, prop.value1, prop.value2, prop.axis, prop.isAngular);
} }
else else {
{
con.setGenericConstraintLimits(true, prop.value1, prop.value2, prop.axis, prop.isAngular); con.setGenericConstraintLimits(true, prop.value1, prop.value2, prop.axis, prop.isAngular);
} }
} }
} }
pivotObject.addTrait(con); pivotObject.addTrait(con);
} }
#end #end
runOutput(0); runOutput(0);
} }
} }

View file

@ -1,15 +1,17 @@
package armory.logicnode; package armory.logicnode;
import iron.object.Object;
#if arm_physics #if arm_physics
import armory.trait.physics.RigidBody;
import armory.trait.physics.bullet.RigidBody.Shape; import armory.trait.physics.bullet.RigidBody.Shape;
#end #end
import iron.object.Object;
import armory.trait.physics.RigidBody;
class AddRigidBodyNode extends LogicNode { class AddRigidBodyNode extends LogicNode {
public var property0: String;//Shape public var property0: String; //Shape
public var property1: String;//Advanced public var property1: Bool; //Advanced
public var object: Object; public var object: Object;
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
@ -18,6 +20,10 @@ class AddRigidBodyNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
object = inputs[1].get(); object = inputs[1].get();
if (object == null) return;
#if arm_physics
var mass: Float = inputs[2].get(); var mass: Float = inputs[2].get();
var active: Bool = inputs[3].get(); var active: Bool = inputs[3].get();
var animated: Bool = inputs[4].get(); var animated: Bool = inputs[4].get();
@ -38,8 +44,7 @@ class AddRigidBodyNode extends LogicNode {
var shape: Shape = 1; var shape: Shape = 1;
if(property1 == 'true') if (property1) {
{
margin = inputs[9].get(); margin = inputs[9].get();
marginLen = inputs[10].get(); marginLen = inputs[10].get();
linDamp = inputs[11].get(); linDamp = inputs[11].get();
@ -49,50 +54,34 @@ class AddRigidBodyNode extends LogicNode {
angVelThreshold = inputs[15].get(); angVelThreshold = inputs[15].get();
group = inputs[16].get(); group = inputs[16].get();
mask = inputs[17].get(); mask = inputs[17].get();
} }
if (object == null) return;
#if arm_physics
var rb: RigidBody = object.getTrait(RigidBody); var rb: RigidBody = object.getTrait(RigidBody);
if((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32 if ((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32
if((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32 if ((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32
if(rb == null) if (rb == null) {
{ switch (property0) {
case "Box": shape = Box;
switch (property0){ case "Sphere": shape = Sphere;
case "Capsule": shape = Capsule;
case 'Box': case "Cone": shape = Cone;
shape = Box; case "Cylinder": shape = Cylinder;
case 'Sphere': case "Convex Hull": shape = ConvexHull;
shape = Sphere; case "Mesh": shape = Mesh;
case 'Capsule':
shape = Capsule;
case 'Cone':
shape = Cone;
case 'Cylinder':
shape = Cylinder;
case 'Convex Hull':
shape = ConvexHull;
case 'Mesh':
shape = Mesh;
} }
rb = new RigidBody(shape, mass, friction, bounciness, group, mask); rb = new RigidBody(shape, mass, friction, bounciness, group, mask);
rb.animated = animated; rb.animated = animated;
rb.staticObj = ! active; rb.staticObj = !active;
rb.isTriggerObject(trigger); rb.isTriggerObject(trigger);
if(property1 == 'true')
{ if (property1) {
rb.linearDamping = linDamp; rb.linearDamping = linDamp;
rb.angularDamping = angDamp; rb.angularDamping = angDamp;
if(margin) rb.collisionMargin = marginLen; if (margin) rb.collisionMargin = marginLen;
if(useDeactiv) { if (useDeactiv) {
rb.setUpDeactivation(true, linearVelThreshold, angVelThreshold, 0.0); rb.setUpDeactivation(true, linearVelThreshold, angVelThreshold, 0.0);
} }
} }
object.addTrait(rb); object.addTrait(rb);

View file

@ -10,10 +10,17 @@ class AddTraitNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var object: Object = inputs[1].get(); var object: Object = inputs[1].get();
var trait: Dynamic = inputs[2].get(); var traitName: String = inputs[2].get();
if (object == null || trait == null) return; assert(Error, object != null, "Object should not be null");
assert(Error, traitName != null, "Trait name should not be null");
var cname = Type.resolveClass(Main.projectPackage + "." + traitName);
if (cname == null) cname = Type.resolveClass(Main.projectPackage + ".node." + traitName);
assert(Error, cname != null, 'No trait with the name "$traitName" found, make sure that the trait is exported!');
assert(Warning, object.getTrait(cname) == null, 'Object already has the trait "$traitName" applied');
var trait = Type.createInstance(cname, []);
object.addTrait(trait); object.addTrait(trait);
runOutput(0); runOutput(0);

View file

@ -12,11 +12,17 @@ class CallHaxeStaticNode extends LogicNode {
var path: String = inputs[1].get(); var path: String = inputs[1].get();
if (path != "") { if (path != "") {
var args: Array<Dynamic> = [];
for (i in 2...inputs.length) {
args.push(inputs[i].get());
}
var dotIndex = path.lastIndexOf("."); var dotIndex = path.lastIndexOf(".");
var classPath = path.substr(0, dotIndex); var classPath = path.substr(0, dotIndex);
var classType = Type.resolveClass(classPath); var classType = Type.resolveClass(classPath);
var funName = path.substr(dotIndex + 1); var funName = path.substr(dotIndex + 1);
result = Reflect.callMethod(classType, Reflect.field(classType, funName), [tree]); result = Reflect.callMethod(classType, Reflect.field(classType, funName), args);
} }
runOutput(0); runOutput(0);

View file

@ -0,0 +1,39 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
import kha.Color;
import iron.math.Vec4;
class CanvasSetProgressBarColorNode extends LogicNode {
var canvas: CanvasScript;
var element: String;
var color = new Vec4();
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
function update() {
if (!canvas.ready) return;
tree.removeUpdate(update);
var e = canvas.getElement(element);
if (e != null) e.color_progress = Color.fromFloats(color.x, color.y, color.z, color.w);
runOutput(0);
}
override function run(from: Int) {
element = inputs[1].get();
color = inputs[2].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
// Ensure canvas is ready
tree.notifyOnUpdate(update);
update();
}
#end
}

View file

@ -19,9 +19,11 @@ class CompareNode extends LogicNode {
switch (property0) { switch (property0) {
case "Equal": case "Equal":
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2; cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
case "Not Equal":
cond = Std.isOfType(v1, Vec4) ? !v1.equals(v2) : v1 != v2;
case "Almost Equal": case "Almost Equal":
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
case "Greater": case "Greater":
cond = v1 > v2; cond = v1 > v2;
case "Greater Equal": case "Greater Equal":
@ -30,6 +32,9 @@ class CompareNode extends LogicNode {
cond = v1 < v2; cond = v1 < v2;
case "Less Equal": case "Less Equal":
cond = v1 <= v2; cond = v1 <= v2;
case "Between":
var v3: Dynamic = inputs[2].get();
cond = v2 <= v1 && v1 <= v3;
case "Or": case "Or":
for (input in inputs) { for (input in inputs) {
if (input.get()) { if (input.get()) {

View file

@ -18,9 +18,11 @@ class GateNode extends LogicNode {
switch (property0) { switch (property0) {
case "Equal": case "Equal":
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2; cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
case "Not Equal":
cond = Std.isOfType(v1, Vec4) ? !v1.equals(v2) : v1 != v2;
case "Almost Equal": case "Almost Equal":
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
case "Greater": case "Greater":
cond = v1 > v2; cond = v1 > v2;
case "Greater Equal": case "Greater Equal":
@ -29,6 +31,9 @@ class GateNode extends LogicNode {
cond = v1 < v2; cond = v1 < v2;
case "Less Equal": case "Less Equal":
cond = v1 <= v2; cond = v1 <= v2;
case "Between":
var v3: Dynamic = inputs[3].get();
cond = v2 <= v1 && v1 <= v3;
case "Or": case "Or":
for (i in 1...inputs.length) { for (i in 1...inputs.length) {
if (inputs[i].get()) { if (inputs[i].get()) {

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.object.Object;
class GetAgentDataNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Float {
var object: Object = inputs[0].get();
assert(Error, object != null, "The object to naviagte should not be null");
#if arm_navigation
var agent: armory.trait.NavAgent = object.getTrait(armory.trait.NavAgent);
assert(Error, agent != null, "The object does not have NavAgent Trait");
if(from == 0) return agent.speed;
else return agent.turnDuration;
#else
return null;
#end
}
}

View file

@ -23,7 +23,7 @@ class GetBoneFkIkOnlyNode extends LogicNode {
var bone = anim.getBone(boneName); var bone = anim.getBone(boneName);
//Get bone transform in world coordinates //Get bone transform in world coordinates
return bone.is_IK_FK_only; return bone.is_ik_fk_only;
#end #end

View file

@ -0,0 +1,33 @@
package armory.logicnode;
import iron.system.Input;
class GetGamepadStartedNode extends LogicNode {
var buttonStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var g = Input.getGamepad(inputs[0].get());
buttonStarted = null;
for (b in Gamepad.buttons) {
if (g.started(b)) {
buttonStarted = b;
break;
}
}
if (buttonStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return buttonStarted;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class GetGlobalCanvasFontSizeNode extends LogicNode {
var canvas: CanvasScript;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function get(from: Int): Dynamic {
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
return canvas.getCanvasFontSize();
}
#end
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class GetGlobalCanvasScaleNode extends LogicNode {
var canvas: CanvasScript;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function get(from: Int): Dynamic {
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
return canvas.getUiScale();
}
#end
}

View file

@ -0,0 +1,24 @@
package armory.logicnode;
import armory.system.InputMap;
class GetInputMapKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var inputMap = inputs[0].get();
var key = inputs[1].get();
var k = InputMap.getInputMapKey(inputMap, key);
if (k != null) {
if (from == 0) return k.scale;
else if (from == 1) return k.deadzone;
}
return null;
}
}

View file

@ -0,0 +1,32 @@
package armory.logicnode;
import iron.system.Input;
class GetKeyboardStartedNode extends LogicNode {
var kb = Input.getKeyboard();
var keyStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
keyStarted = null;
for (k in Keyboard.keys) {
if (kb.started(k)) {
keyStarted = k;
break;
}
}
if (keyStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return keyStarted;
}
}

View file

@ -10,9 +10,22 @@ class GetLocationNode extends LogicNode {
override function get(from: Int): Dynamic { override function get(from: Int): Dynamic {
var object: Object = inputs[0].get(); var object: Object = inputs[0].get();
var relative: Bool = inputs[1].get();
if (object == null) return null; if (object == null) return null;
return object.transform.world.getLoc(); var loc = object.transform.world.getLoc();
if (relative && object.parent != null) {
loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence
// Convert loc to parent local space
var dotX = loc.dot(object.parent.transform.right());
var dotY = loc.dot(object.parent.transform.look());
var dotZ = loc.dot(object.parent.transform.up());
loc.set(dotX, dotY, dotZ);
}
return loc;
} }
} }

View file

@ -1,6 +1,7 @@
package armory.logicnode; package armory.logicnode;
import iron.object.MeshObject; import iron.object.MeshObject;
import iron.object.DecalObject;
class GetMaterialNode extends LogicNode { class GetMaterialNode extends LogicNode {
@ -9,11 +10,27 @@ class GetMaterialNode extends LogicNode {
} }
override function get(from: Int): Dynamic { override function get(from: Int): Dynamic {
var object: MeshObject = inputs[0].get();
var slot: Int = inputs[1].get();
if (object == null) return null; var object = inputs[0].get();
return object.materials[slot]; assert(Error, object != null, "The object input must not be null");
#if rp_decals
if (Std.isOfType(object, DecalObject)) {
var decal = cast(object, DecalObject);
return decal.material;
}
#end
if (Std.isOfType(object, MeshObject)) {
var mesh = cast(object, MeshObject);
var slot: Int = inputs[1].get();
if (mesh == null) return null;
return mesh.materials[slot];
}
return null;
} }
} }

View file

@ -0,0 +1,32 @@
package armory.logicnode;
import iron.system.Input;
class GetMouseStartedNode extends LogicNode {
var m = Input.getMouse();
var buttonStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
buttonStarted = null;
for (b in Mouse.buttons) {
if (m.started(b)) {
buttonStarted = b;
break;
}
}
if (buttonStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return buttonStarted;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.data.SceneFormat;
import iron.object.Object;
class GetObjectByUidNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var objectUid: Int = inputs[0].get();
var obj = iron.Scene.active.getChildren(true);
for (obji in obj) if (obji.uid == objectUid) return obji;
return null;
}
}

View file

@ -1,10 +1,13 @@
package armory.logicnode; package armory.logicnode;
import iron.object.Object; import iron.object.Object;
import iron.math.Vec3; import iron.math.Quat;
import iron.math.Vec4;
class GetRotationNode extends LogicNode { class GetRotationNode extends LogicNode {
public var property0: String;
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
} }
@ -16,34 +19,16 @@ class GetRotationNode extends LogicNode {
return null; return null;
} }
var rot = object.transform.rot;
switch (from) { switch(property0){
case 0: case "Local":
// euler angles return object.transform.rot;
return object.transform.rot.getEuler(); case "Global":{
case 1: var useless1 = new Vec4();
// vector var ret = new Quat();
var sqrtW = Math.sqrt(1 - (rot.w * rot.w)); object.transform.world.decompose(useless1, ret, useless1);
if (sqrtW == 0) { return ret;
return new Vec3(0, 0, 1); }}
}
return new Vec3(rot.x / sqrtW, rot.y / sqrtW, rot.z / sqrtW);
case 2:
// angle radians
var angle = 2 * Math.acos(rot.w);
return angle;
case 3:
// angle degrees
var angle = 2 * Math.acos(rot.w);
return angle * (180 / Math.PI);
case 4:
//quaternion xyz
return new Vec3(rot.x, rot.y, rot.z);
case 5:
//quaternion w
return rot.w;
}
return null; return null;
} }
} }

View file

@ -16,12 +16,12 @@ class GetTraitNameNode extends LogicNode {
case 0: { case 0: {
// Check CanvasScript // Check CanvasScript
var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript"); var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript");
if (Std.is(trait, cname)) { if (Std.isOfType(trait, cname)) {
return trait.cnvName; return trait.cnvName;
} }
// Check WasmScript // Check WasmScript
var cname = cast Type.resolveClass("armory.trait.internal.WasmScript"); var cname = cast Type.resolveClass("armory.trait.internal.WasmScript");
if (Std.is(trait, cname)) { if (Std.isOfType(trait, cname)) {
return trait.wasmName; return trait.wasmName;
} }
// Other // Other

View file

@ -0,0 +1,18 @@
package armory.logicnode;
import iron.object.Object;
class GetUidNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var object: Object = inputs[0].get();
if (object == null) return null;
return object.uid;
}
}

View file

@ -13,15 +13,26 @@ class GoToLocationNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var object: Object = inputs[1].get(); var object: Object = inputs[1].get();
var location: Vec4 = inputs[2].get(); var location: Vec4 = inputs[2].get();
var speed: Float = inputs[3].get();
var turnDuration: Float = inputs[4].get();
if (object == null || location == null) return; assert(Error, object != null, "The object input not be null");
assert(Error, location != null, "The location to navigate to must not be null");
assert(Error, speed != null, "Speed of Nav Agent should not be null");
assert(Warning, speed >= 0, "Speed of Nav Agent should be positive");
assert(Error, turnDuration != null, "Turn Duration of Nav Agent should not be null");
assert(Warning, turnDuration >= 0, "Turn Duration of Nav Agent should be positive");
#if arm_navigation #if arm_navigation
// Assume navmesh exists..
var from = object.transform.world.getLoc(); var from = object.transform.world.getLoc();
var to = location; var to = location;
assert(Error, Navigation.active.navMeshes.length > 0, "No Navigation Mesh Present");
Navigation.active.navMeshes[0].findPath(from, to, function(path: Array<iron.math.Vec4>) { Navigation.active.navMeshes[0].findPath(from, to, function(path: Array<iron.math.Vec4>) {
var agent: armory.trait.NavAgent = object.getTrait(armory.trait.NavAgent); var agent: armory.trait.NavAgent = object.getTrait(armory.trait.NavAgent);
assert(Error, agent != null, "Object does not have a NavAgent trait");
agent.speed = speed;
agent.turnDuration = turnDuration;
agent.setPath(path); agent.setPath(path);
}); });
#end #end

View file

@ -1,31 +1,136 @@
package armory.logicnode; package armory.logicnode;
#if arm_patch @:keep @:keepSub #end
class LogicNode { class LogicNode {
var tree: LogicTree; var tree: LogicTree;
var inputs: Array<LogicNodeInput> = []; var inputs: Array<LogicNodeLink> = [];
var outputs: Array<Array<LogicNode>> = []; var outputs: Array<Array<LogicNodeLink>> = [];
#if arm_debug #if (arm_debug || arm_patch)
public var name = ""; public var name = "";
public function watch(b: Bool) { // Watch in debug console
var nodes = armory.trait.internal.DebugConsole.watchNodes; #if (arm_debug)
b ? nodes.push(this) : nodes.remove(this); public function watch(b: Bool) { // Watch in debug console
} var nodes = armory.trait.internal.DebugConsole.watchNodes;
b ? nodes.push(this) : nodes.remove(this);
}
#end
#end #end
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
this.tree = tree; this.tree = tree;
} }
public function addInput(node: LogicNode, from: Int) { /**
inputs.push(new LogicNodeInput(node, from)); Resize the inputs array to a given size to minimize dynamic
reallocation and over-allocation later.
**/
inline function preallocInputs(amount: Int) {
this.inputs.resize(amount);
} }
public function addOutputs(nodes: Array<LogicNode>) { /**
outputs.push(nodes); Resize the outputs array to a given size to minimize dynamic
reallocation and over-allocation later.
**/
inline function preallocOutputs(amount: Int) {
this.outputs.resize(amount);
for (i in 0...outputs.length) {
outputs[i] = [];
}
} }
/**
Add a link between to nodes to the tree.
**/
public static function addLink(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int): LogicNodeLink {
var link = new LogicNodeLink(fromNode, toNode, fromIndex, toIndex);
if (toNode.inputs.length <= toIndex) {
toNode.inputs.resize(toIndex + 1);
}
toNode.inputs[toIndex] = link;
var fromNodeOuts = fromNode.outputs;
var outLen = fromNodeOuts.length;
if (outLen <= fromIndex) {
fromNodeOuts.resize(fromIndex + 1);
// Initialize with empty arrays
for (i in outLen...fromIndex + 1) {
fromNodeOuts[i] = [];
}
}
fromNodeOuts[fromIndex].push(link);
return link;
}
#if arm_patch
/**
Removes a link from the tree.
**/
static function removeLink(link: LogicNodeLink) {
link.fromNode.outputs[link.fromIndex].remove(link);
// Reuse the same link and connect a default input node to it.
// That's why this function is only available in arm_patch mode, we need
// access to the link's type and value.
link.fromNode = LogicNode.createSocketDefaultNode(link.toNode.tree, link.toType, link.toValue);
link.fromIndex = 0;
}
/**
Removes all inputs and their links from this node.
Warning: this function changes the amount of node inputs to 0!
**/
function clearInputs() {
for (link in inputs) {
link.fromNode.outputs[link.fromIndex].remove(link);
}
inputs.resize(0);
}
/**
Removes all outputs and their links from this node.
Warning: this function changes the amount of node inputs to 0!
**/
function clearOutputs() {
for (links in outputs) {
for (link in links) {
var defaultNode = LogicNode.createSocketDefaultNode(tree, link.toType, link.toValue);
link.fromNode = defaultNode;
link.fromIndex = 0;
defaultNode.outputs[0] = [link];
}
}
outputs.resize(0);
}
/**
Creates a default node for a socket so that get() and set() can be
used without null checks.
Loosely equivalent to `make_logic.build_default_node()` in Python.
**/
static inline function createSocketDefaultNode(tree: LogicTree, socketType: String, value: Dynamic): LogicNode {
// Make sure to not add these nodes to the LogicTree.nodes array as they
// won't be garbage collected then if unlinked later.
return switch (socketType) {
case "VECTOR": new armory.logicnode.VectorNode(tree, value[0], value[1], value[2]);
case "RGBA": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2], value[3]);
case "RGB": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2]);
case "VALUE": new armory.logicnode.FloatNode(tree, value);
case "INT": new armory.logicnode.IntegerNode(tree, value);
case "BOOLEAN": new armory.logicnode.BooleanNode(tree, value);
case "STRING": new armory.logicnode.StringNode(tree, value);
case "NONE": new armory.logicnode.NullNode(tree);
case "OBJECT": new armory.logicnode.ObjectNode(tree, value);
default: new armory.logicnode.DynamicNode(tree, value);
}
}
#end
/** /**
Called when this node is activated. Called when this node is activated.
@param from impulse index @param from impulse index
@ -38,42 +143,45 @@ class LogicNode {
**/ **/
function runOutput(i: Int) { function runOutput(i: Int) {
if (i >= outputs.length) return; if (i >= outputs.length) return;
for (o in outputs[i]) { for (outLink in outputs[i]) {
// Check which input activated the node outLink.toNode.run(outLink.toIndex);
for (j in 0...o.inputs.length) {
if (o.inputs[j].node == this) {
o.run(j);
break;
}
}
} }
} }
@:allow(armory.logicnode.LogicNodeInput) @:allow(armory.logicnode.LogicNodeLink)
function get(from: Int): Dynamic { return this; } function get(from: Int): Dynamic { return this; }
@:allow(armory.logicnode.LogicNodeInput) @:allow(armory.logicnode.LogicNodeLink)
function set(value: Dynamic) {} function set(value: Dynamic) {}
} }
class LogicNodeInput { @:allow(armory.logicnode.LogicNode)
@:allow(armory.logicnode.LogicTree)
class LogicNodeLink {
@:allow(armory.logicnode.LogicNode) var fromNode: LogicNode;
var node: LogicNode; var toNode: LogicNode;
var from: Int; // Socket index var fromIndex: Int;
var toIndex: Int;
public function new(node: LogicNode, from: Int) { #if arm_patch
this.node = node; var fromType: String;
this.from = from; var toType: String;
var toValue: Dynamic;
#end
inline function new(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int) {
this.fromNode = fromNode;
this.toNode = toNode;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
} }
@:allow(armory.logicnode.LogicNode) inline function get(): Dynamic {
function get(): Dynamic { return fromNode.get(fromIndex);
return node.get(from);
} }
@:allow(armory.logicnode.LogicNode) inline function set(value: Dynamic) {
function set(value: Dynamic) { fromNode.set(value);
node.set(value);
} }
} }

View file

@ -2,10 +2,26 @@ package armory.logicnode;
class LogicTree extends iron.Trait { class LogicTree extends iron.Trait {
#if arm_patch
/**
Stores all trait instances of the tree via its name.
**/
public static var nodeTrees = new Map<String, Array<LogicTree>>();
/**
[node name => logic node] for later node replacement for live patching.
**/
public var nodes: Map<String, LogicNode>;
#end
public var loopBreak = false; // Trigger break from loop nodes public var loopBreak = false; // Trigger break from loop nodes
public function new() { public function new() {
super(); super();
#if arm_patch
nodes = new Map<String, LogicNode>();
#end
} }
public function add() {} public function add() {}

View file

@ -37,6 +37,6 @@ class LookAtNode extends LogicNode {
v2.setFrom(vto).sub(vfrom).normalize(); v2.setFrom(vto).sub(vfrom).normalize();
q.fromTo(v1, v2); q.fromTo(v1, v2);
return q.getEuler(); return q;
} }
} }

View file

@ -3,7 +3,7 @@ package armory.logicnode;
class MathNode extends LogicNode { class MathNode extends LogicNode {
public var property0: String; // Operation public var property0: String; // Operation
public var property1: String; // Clamp public var property1: Bool; // Clamp
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
@ -80,8 +80,8 @@ class MathNode extends LogicNode {
} }
} }
// Clamp // Clamp
if (property1 == "true") r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r); if (property1) r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r);
return r; return r;
} }
} }

View file

@ -6,7 +6,7 @@ class MixNode extends LogicNode {
public var property0: String; // Type public var property0: String; // Type
public var property1: String; // Ease public var property1: String; // Ease
public var property2: String; // Clamp public var property2: Bool; // Clamp
var ease: Float->Float = null; var ease: Float->Float = null;
@ -50,7 +50,9 @@ class MixNode extends LogicNode {
var v2: Float = inputs[2].get(); var v2: Float = inputs[2].get();
var f = v1 + (v2 - v1) * ease(k); var f = v1 + (v2 - v1) * ease(k);
if (property2 == "true") f = f < 0 ? 0 : f > 1 ? 1 : f; // Clamp
if (property2) f = f < 0 ? 0 : f > 1 ? 1 : f;
return f; return f;
} }
} }

View file

@ -21,7 +21,7 @@ class ObjectNode extends LogicNode {
override function set(value: Dynamic) { override function set(value: Dynamic) {
if (inputs.length > 0) inputs[0].set(value); if (inputs.length > 0) inputs[0].set(value);
else { else {
objectName = value.name; objectName = value != null ? value.name : "";
this.value = value; this.value = value;
} }
} }

View file

@ -4,7 +4,7 @@ import armory.trait.internal.CanvasScript;
import iron.Scene; import iron.Scene;
#if arm_ui #if arm_ui
import zui.Canvas.Anchor; import armory.ui.Canvas.Anchor;
#end #end
class OnCanvasElementNode extends LogicNode { class OnCanvasElementNode extends LogicNode {

View file

@ -0,0 +1,35 @@
package armory.logicnode;
import armory.system.InputMap;
class OnInputMapNode extends LogicNode {
var inputMap: Null<InputMap>;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
function update() {
var i = inputs[0].get();
inputMap = InputMap.getInputMap(i);
if (inputMap != null) {
if (inputMap.started()) {
runOutput(0);
}
if (inputMap.released()) {
runOutput(1);
}
}
}
override function get(from: Int): Dynamic {
if (from == 2) return inputMap.value();
else return inputMap.lastKeyPressed;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
class OncePerFrameNode extends LogicNode {
var c = false;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
override function run(from: Int) {
if(c) {
c = false;
runOutput(0);
}
}
function update() {
c = true;
}
}

View file

@ -8,7 +8,7 @@ class PauseTraitNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var trait: Dynamic = inputs[1].get(); var trait: Dynamic = inputs[1].get();
if (trait == null || !Std.is(trait, LogicTree)) return; if (trait == null || !Std.isOfType(trait, LogicTree)) return;
cast(trait, LogicTree).pause(); cast(trait, LogicTree).pause();

View file

@ -3,50 +3,39 @@ package armory.logicnode;
#if arm_physics #if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis; import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end #end
import iron.object.Object;
class PhysicsConstraintNode extends LogicNode { class PhysicsConstraintNode extends LogicNode {
public var property0: String;//Linear or Angular public var property0: String; //Linear or Angular
public var property1: String;//Axis public var property1: String; //Axis
public var property2: String;//Is a spring public var property2: Bool; //Is a spring
public var value1: Float;//Lower limit or Spring Stiffness
public var value2: Float;//Upper limit or Spring Damping #if arm_physics
public var value1: Float; //Lower limit or Spring Stiffness
public var value2: Float; //Upper limit or Spring Damping
public var isAngular: Bool; public var isAngular: Bool;
public var axis: ConstraintAxis; public var axis: ConstraintAxis;
public var isSpring: Bool; public var isSpring: Bool;
#end
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
} }
override function get(from: Int): PhysicsConstraintNode { override function get(from: Int): PhysicsConstraintNode {
#if arm_physics
value1 = inputs[0].get(); value1 = inputs[0].get();
value2 = inputs[1].get(); value2 = inputs[1].get();
if(property0 == 'Linear') { isAngular = property0 != "Linear";
isAngular = false; isSpring = property2;
}
else{
isAngular = true;
}
if(property2 == 'true'){ switch (property1) {
isSpring = true; case "X": axis = X;
case "Y": axis = Y;
case "Z": axis = Z;
} }
else { #end
isSpring = false;
}
switch (property1){
case 'X':
axis = X;
case 'Y':
axis = Y;
case 'Z':
axis = Z;
}
return this; return this;
} }
} }

View file

@ -4,8 +4,6 @@ import iron.math.Vec4;
class PickObjectNode extends LogicNode { class PickObjectNode extends LogicNode {
var v = new Vec4();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
} }
@ -24,9 +22,14 @@ class PickObjectNode extends LogicNode {
if (from == 0) { // Object if (from == 0) { // Object
return rb.object; return rb.object;
} }
else { // Hit else if(from == 1){ // Hit
var v = new Vec4();
return v.set(physics.hitPointWorld.x, physics.hitPointWorld.y, physics.hitPointWorld.z); return v.set(physics.hitPointWorld.x, physics.hitPointWorld.y, physics.hitPointWorld.z);
} }
else { // Normal
var v = new Vec4();
return v.set(physics.hitNormalWorld.x, physics.hitNormalWorld.y, physics.hitNormalWorld.z, 0);
}
#end #end
return null; return null;
} }

View file

@ -19,7 +19,7 @@ class QuaternionMathNode extends LogicNode {
override function get(from: Int): Dynamic { override function get(from: Int): Dynamic {
switch (property0) { switch (property0) {
// 1 argument: Module, Normalize, GetEuler // 1 argument: Module, Normalize
case "Module": { case "Module": {
var q: Quat = inputs[0].get(); var q: Quat = inputs[0].get();
if (q == null) return null; if (q == null) return null;
@ -32,159 +32,106 @@ class QuaternionMathNode extends LogicNode {
res_q.setFrom(q); res_q.setFrom(q);
res_q = res_q.normalize(); res_q = res_q.normalize();
} }
case "GetEuler": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
res_v = res_q.getEuler();
}
// 2 arguments: FromTo, FromMat, FromRotationMat, ToAxisAngle
case "FromTo": {
var v1: Vec4 = inputs[0].get();
var v2: Vec4 = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.fromTo(v1, v2);
}
case "FromMat": {
var q: Quat = inputs[0].get();
var m: Mat4 = inputs[1].get();
if ((q == null) || (m == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromMat(m);
}
case "FromRotationMat": {
var q: Quat = inputs[0].get();
var m: Mat4 = inputs[1].get();
if ((q == null) || (m == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromRotationMat(m);
}
case "ToAxisAngle": {
var q: Quat = inputs[0].get();
var v: Vec4 = inputs[1].get();
if ((q == null) || (v == null)) return null;
res_q.setFrom(q);
res_f = res_q.toAxisAngle(v);
}
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
case "Lerp": {
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (to == null)) return null;
res_q = res_q.lerp(from, to, f);
}
case "Slerp": {
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (to == null)) return null;
res_q = res_q.slerp(from, to, f);
}
case "FromAxisAngle": {
var q: Quat = inputs[0].get();
var axis: Vec4 = inputs[1].get();
var angle: Float = inputs[2].get();
if ((q == null) || (axis == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromAxisAngle(axis, angle);
}
case "FromEuler": {
var x: Float = inputs[0].get();
var y: Float = inputs[1].get();
var z: Float = inputs[2].get();
res_q = res_q.fromEuler(x, y, z);
}
// Many arguments: Add, Subtract, DotProduct, Multiply // Many arguments: Add, Subtract, DotProduct, Multiply
case "Add": { case "Add": {
var q: Quat = inputs[0].get(); res_v = inputs[0].get();
if (q == null) return null; res_f = inputs[1].get();
res_q.setFrom(q); if (res_v == null || res_f == null) return null;
var q2 = new Quat();
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1; var i = 1;
while (i < inputs.length) { while (2*i+1 < inputs.length) {
q2 = inputs[i].get(); res_v = inputs[2*i].get();
if (q2 == null) return null; res_f = inputs[2*i+1].get();
res_q.add(q2); if (res_v == null || res_f == null) return null;
res_q.x += res_v.x;
res_q.y += res_v.y;
res_q.z += res_v.z;
res_q.w += res_f;
i++; i++;
} }
} }
case "Subtract": { case "Subtract": {
var q: Quat = inputs[0].get(); res_v = inputs[0].get();
if (q == null) return null; res_f = inputs[1].get();
res_q.setFrom(q); if (res_v == null || res_f == null) return null;
var q2 = new Quat();
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1; var i = 1;
while (i < inputs.length) { while (2*i+1 < inputs.length) {
q2 = inputs[i].get(); res_v = inputs[2*i].get();
if (q2 == null) return null; res_f = inputs[2*i+1].get();
res_q.sub(q2); if (res_v == null || res_f == null) return null;
res_q.x -= res_v.x;
res_q.y -= res_v.y;
res_q.z -= res_v.z;
res_q.w -= res_f;
i++; i++;
} }
} }
case "Multiply": { case "Multiply": {
var q: Quat = inputs[0].get(); res_v = inputs[0].get();
if (q == null) return null; res_f = inputs[1].get();
res_q.setFrom(q); if (res_v == null || res_f == null) return null;
var q2 = new Quat();
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1; var i = 1;
while (i < inputs.length) { while (2*i+1 < inputs.length) {
q2 = inputs[i].get(); res_v = inputs[2*i].get();
if (q2 == null) return null; res_f = inputs[2*i+1].get();
res_q.mult(q2); if (res_v == null || res_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, res_f);
res_q.mult(temp_q);
i++; i++;
} }
} }
case "MultiplyFloats": { case "MultiplyFloats": {
var q: Quat = inputs[0].get(); res_v = inputs[0].get();
if (q == null) return null; res_f = inputs[1].get();
res_q.setFrom(q); if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var f: Float = 1.0; var f: Float = 1.0;
var i = 1; var i = 2;
while (i < inputs.length) { while (i < inputs.length) {
f = inputs[i].get(); f *= inputs[i].get();
res_q.scale(f); if (f == null) return null;
i++; i++;
} }
res_q.scale(f);
} }
case "DotProduct": { case "DotProduct": { // what this does with more than 2 terms is not *remotely* intuitive. Heck, you could consider it a footgun!
var q: Quat = inputs[0].get();
if (q == null) return null; res_v = inputs[0].get();
res_q.setFrom(q); var temp_f = inputs[1].get();
var q2 = new Quat(); if (res_v == null || temp_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, temp_f);
var i = 1; var i = 1;
while (i < inputs.length) { while (2*i+1 < inputs.length) {
q2 = inputs[i].get(); res_v = inputs[2*i].get();
if (q2 == null) return null; temp_f = inputs[2*i+1].get();
res_f = res_q.dot(q2); if (res_v == null || temp_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, temp_f);
res_f = res_q.dot(temp_q);
res_q.set(res_f, res_f, res_f, res_f); res_q.set(res_f, res_f, res_f, res_f);
i++; i++;
} }
} }
} }
// Return and check separator
switch (from) { switch (from) {
case 0: { case 0: {
if (property0 == 'GetEuler') return res_q;
return res_v;
else
return res_q;
} }
case 1: case 1:
if (property1) { if (property0 == "DotProduct" || property0 == "Module") {
return res_q.x;
} else {
return res_f; return res_f;
} else {
return null;
} }
case 2: default: {
if (property1) return res_q.y; return null;
case 3: }
if (property1) return res_q.z;
case 4:
if (property1) return res_q.w;
case 5:
if (property1) return res_f;
} }
return null;
} }
} }

View file

@ -11,10 +11,10 @@ class QuaternionNode extends LogicNode {
super(tree); super(tree);
if (x != null) { if (x != null) {
addInput(new FloatNode(tree, x), 0); LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
addInput(new FloatNode(tree, y), 0); LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
addInput(new FloatNode(tree, z), 0); LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
addInput(new FloatNode(tree, w), 0); LogicNode.addLink(new FloatNode(tree, w), this, 0, 3);
} }
} }
@ -27,15 +27,15 @@ class QuaternionNode extends LogicNode {
switch (from){ switch (from){
case 0: case 0:
return value; return value;
case 1: case 1:
var value1 = new Vec4(); var value1 = new Vec4();
value1.x = value.x; value1.x = value.x;
value1.y = value.y; value1.y = value.y;
value1.z = value.z; value1.z = value.z;
value1.w = 0; // use 0 to avoid this vector being translated. value1.w = 0; // use 0 to avoid this vector being translated.
return value1; return value1;
case 2: case 2:
return value.w; return value.w;
default: default:
return null; return null;
} }

View file

@ -4,18 +4,17 @@ import iron.math.Vec4;
class RandomColorNode extends LogicNode { class RandomColorNode extends LogicNode {
var v = new Vec4();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
} }
override function get(from: Int): Dynamic { override function get(from: Int): Dynamic {
var r = Math.random(); var r = Math.random();
var g = Math.random(); var g = Math.random();
var b = Math.random(); var b = Math.random();
// var a = Math.random(); // var a = Math.random();
v.set(r, g, b); var v = new Vec4(r, g, b);
return v; return v;
} }
} }

View file

@ -0,0 +1,19 @@
package armory.logicnode;
import armory.system.InputMap;
class RemoveInputMapKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var inputMap = inputs[1].get();
var key = inputs[2].get();
if (InputMap.removeInputMapKey(inputMap, key)) {
runOutput(0);
}
}
}

View file

@ -8,7 +8,7 @@ class ResumeTraitNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var trait: Dynamic = inputs[1].get(); var trait: Dynamic = inputs[1].get();
if (trait == null || !Std.is(trait, LogicTree)) return; if (trait == null || !Std.isOfType(trait, LogicTree)) return;
cast(trait, LogicTree).resume(); cast(trait, LogicTree).resume();

View file

@ -2,13 +2,11 @@ package armory.logicnode;
import iron.object.Object; import iron.object.Object;
import iron.math.Quat; import iron.math.Quat;
import iron.math.Vec4;
import armory.trait.physics.RigidBody; import armory.trait.physics.RigidBody;
class RotateObjectNode extends LogicNode { class RotateObjectNode extends LogicNode {
public var property0 = "Euler Angles"; public var property0 = "Local";
var q = new Quat();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
@ -16,32 +14,19 @@ class RotateObjectNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var object: Object = inputs[1].get(); var object: Object = inputs[1].get();
var vec: Vec4 = inputs[2].get(); var q: Quat = inputs[2].get();
// note: here, the next line is disabled because old versions of the node don't have a third input. if (object == null || q == null) return;
// when those old versions will be considered remove, feel free to uncomment that, and replace the other `inputs[3].get()` by `w` in this file.
//var w: Float = inputs[3].get();
if (object == null || vec == null) return; q.normalize();
switch (property0){
switch (property0) { case "Local":
case "Euler Angles": object.transform.rot.mult(q);
q.fromEuler(vec.x, vec.y, vec.z); case "Global":
case "Angle Axies (Degrees)" | "Angle Axies (Radians)": object.transform.rot.multquats(q, object.transform.rot);
var angle: Float = inputs[3].get(); // that function call (Quat.multquats) is weird: it both modifies the object, and returns `this`
if (property0 == "Angle Axies (Degrees)") {
angle = angle * (Math.PI / 180);
}
var angleSin = Math.sin(angle / 2);
vec = vec.normalize();
var angleCos = Math.cos(angle / 2);
q = new Quat(vec.x * angleSin, vec.y * angleSin, vec.z * angleSin, angleCos);
case "Quaternion":
q = new Quat(vec.x, vec.y, vec.z, inputs[3].get());
q.normalize();
} }
object.transform.rot.mult(q);
object.transform.buildMatrix(); object.transform.buildMatrix();
#if arm_physics #if arm_physics

View file

@ -0,0 +1,96 @@
package armory.logicnode;
import iron.math.Quat;
import iron.math.Vec4;
import iron.math.Mat4;
import kha.FastFloat;
class RotationMathNode extends LogicNode {
public var property0: String; // Operation
var res_q = new Quat();
var res_v = new Vec4();
var res_f: FastFloat = 0.0;
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
//var q: Quat = inputs[0].get();
//if (q==null) return null;
//var res_q: Quat = new Quat();
switch (property0) {
// 1 argument: Normalize, Inverse
case "Normalize": {
var q: Quat = inputs[0].get();
if (q==null) return null;
res_q.setFrom(q);
res_q = res_q.normalize();
}
case "Inverse": {
var q: Quat = inputs[0].get();
if (q==null) return null;
var modl = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
modl = -1/modl;
res_q.w = -q.w*modl;
res_q.x = q.x*modl;
res_q.y = q.y*modl;
res_q.z = q.z*modl;
}
// 2 arguments: Compose, Amplify, FromTo, FromRotationMat,
case "FromTo": {
var v1: Vec4 = inputs[0].get();
var v2: Vec4 = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.fromTo(v1, v2);
}
case "Compose": {
var v1: Quat = inputs[0].get();
var v2: Quat = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.multquats(v1,v2);
}
case "Amplify": {
var v1: Quat = inputs[0].get();
var v2: Float = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.setFrom(v1);
var fac2 = Math.sqrt(1- res_q.w*res_q.w);
if (fac2 > 0.001) {
var fac1 = v2*Math.acos(res_q.w);
res_q.w = Math.cos(fac1);
fac1 = Math.sin(fac1)/fac2;
res_q.x *= fac1;
res_q.y *= fac1;
res_q.z *= fac1;
}
}
//case "FromRotationMat": {
// var m: Mat4 = inputs[1].get();
// if (m == null) return null;
// res_q = res_q.fromMat(m);
//}
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
case "Lerp": {
//var from = q;
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.lerp(from, to, f);
}
case "Slerp": {
//var from = q;
var from:Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.slerp(from, to, f);
}
}
return res_q;
}
}

View file

@ -0,0 +1,120 @@
package armory.logicnode;
import iron.math.Vec4;
import iron.math.Quat;
import kha.FastFloat;
class RotationNode extends LogicNode {
static inline var toRAD: FastFloat = 0.017453292519943295; // 180/pi
public var property0: String; // type of input (EulerAngles, AxisAngle, Quaternion)
public var property1: String; // angle unit (Deg, Rad)
public var property2: String; // euler order (XYZ, XZY, etc…)
public var value: Quat;
//var input0_cache: Vec4 = new Vec4();
//var input1_cache: Float = 0;
var input_length: Int = 0;
public function new(tree: LogicTree, x: Null<Float> = null,
y: Null<Float> = null,
z: Null<Float> = null,
w: Null<Float> = null
) {
super(tree);
this.value = new Quat();
if (x!=null) this.value.set(x,y,z,w);
for (input in inputs) {
if (input !=null)
this.input_length +=1;
else
break;
}
}
override function get(from: Int): Dynamic {
//var inp0 = inputs[0].get();
//var inp
//if (inputs[0].get())
if (inputs.length == 0){
return this.value;
}
switch (property0){
case "Quaternion": {
if (inputs[0]!=null && inputs[1]!=null) {
var vect: Vec4 = inputs[0].get();
value.x = vect.x;
value.y = vect.y;
value.z = vect.z;
value.w = inputs[1].get();
}
}
case "AxisAngle": {
if (inputs[0]!=null && inputs[1]!=null){
var vec: Vec4 = inputs[0].get();
var angle: FastFloat = inputs[1].get();
if (property1=="Deg")
angle *= toRAD;
value.fromAxisAngle(vec, angle);
}
}
case "EulerAngles": {
if (inputs[0] != null){
var vec: Vec4 = new Vec4().setFrom(inputs[0].get());
if (property1=="Deg"){
vec.x *= toRAD;
vec.y *= toRAD;
vec.z *= toRAD;
}
this.value.fromEulerOrdered(vec, property2);
}
}
default: {
return property0;
}
}
return this.value;
}
override function set(value: Dynamic) {
switch (property0){
case "Quaternion": {
if (input_length>1) {
var vect = new Vec4();
vect.x = value.x;
vect.y = value.y;
vect.z = value.z;
inputs[0].set(vect);
inputs[1].set(value.w);
}
}
case "AxisAngle": {
if (input_length>1){
var vec = new Vec4();
var angle = this.value.toAxisAngle(vec);
if (property1=="Deg")
angle /= toRAD;
inputs[0].set(vec);
inputs[1].set(angle);
}
}
case "EulerAngles": {
if (input_length>0){
var vec:Vec4 = value.toEulerOrdered(property2);
if (property1=="Deg"){
vec.x /= toRAD;
vec.y /= toRAD;
vec.z /= toRAD;
}
inputs[0].set(vec);
}
}
}
if (input_length > 0){
// NYI
}else this.value=value;
}
}

View file

@ -0,0 +1,37 @@
package armory.logicnode;
class SelectNode extends LogicNode {
/** Execution mode. **/
public var property0: String;
var value: Dynamic = null;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
// Get value according to the activated input (run() can only be called
// if the execution mode is from_input).
value = inputs[from + Std.int(inputs.length / 2)].get();
runOutput(0);
}
override function get(from: Int): Dynamic {
if (property0 == "from_index") {
var index = inputs[0].get() + 2;
// Return default value for invalid index
if (index < 2 || index >= inputs.length) {
return inputs[1].get();
}
return inputs[index].get();
}
// from_input
return value;
}
}

View file

@ -0,0 +1,60 @@
package armory.logicnode;
import kha.FastFloat;
import iron.math.Quat;
import iron.math.Vec4;
class SeparateRotationNode extends LogicNode {
public var property0 = "EulerAngles"; // EulerAngles, AxisAngle, or Quat
public var property1 = "Rad"; // Rad or Deg
public var property2 = "XYZ";
static inline var toDEG:FastFloat = 57.29577951308232; // 180/pi
var input_cache = new Quat();
var euler_cache = new Vec4();
var aa_axis_cache = new Vec4();
var aa_angle_cache: Float = 0;
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var q: Quat = inputs[0].get();
if (q == null) return null;
q.normalize();
switch (property0) {
case "EulerAngles":
if (q!=this.input_cache)
euler_cache = q.toEulerOrdered(property2);
if (from>0)
return null;
switch (property1){
case "Rad": return euler_cache;
case "Deg": return new Vec4(euler_cache.x*toDEG, euler_cache.y*toDEG, euler_cache.z*toDEG);
}
case "AxisAngle":
if (q!=this.input_cache)
aa_angle_cache = q.toAxisAngle(aa_axis_cache);
switch (from){
case 0: return aa_axis_cache;
case 1: switch(property1){
case "Rad": return aa_angle_cache;
case "Deg": return toDEG*aa_angle_cache;
}
}
case "Quaternion":
switch(from){
case 0: return new Vec4(q.x,q.y,q.z);
case 1: return q.w;
}
}
return null;
}
}

View file

@ -20,7 +20,7 @@ class SeparateTransformNode extends LogicNode {
matrix.decompose(loc, rot, scale); matrix.decompose(loc, rot, scale);
if (from == 0) return loc; if (from == 0) return loc;
else if (from == 1) return rot.getEuler(); else if (from == 1) return rot;
else return scale; else return scale;
} }
} }

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class SetGlobalCanvasFontSizeNode extends LogicNode {
var canvas: CanvasScript;
var factor: Int;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function run(from: Int) {
factor = inputs[1].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
canvas.setCanvasFontSize(factor);
runOutput(0);
}
#end
}

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class SetGlobalCanvasScaleNode extends LogicNode {
var canvas: CanvasScript;
var factor: Float;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function run(from: Int) {
factor = inputs[1].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
canvas.setUiScale(factor);
runOutput(0);
}
#end
}

View file

@ -0,0 +1,46 @@
package armory.logicnode;
import armory.system.InputMap;
class SetInputMapKeyNode extends LogicNode {
public var property0: String;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var inputMap = inputs[1].get();
var key = inputs[2].get();
var scale = inputs[3].get();
var deadzone = inputs[4].get();
var index = inputs[5].get();
var i = InputMap.getInputMap(inputMap);
if (i == null) {
i = InputMap.addInputMap(inputMap);
}
var k = InputMap.getInputMapKey(inputMap, key);
if (k == null) {
switch(property0) {
case "keyboard": k = i.addKeyboard(key, scale);
case "mouse": k = i.addMouse(key, scale, deadzone);
case "gamepad": {
k = i.addGamepad(key, scale, deadzone);
k.setIndex(index);
}
}
} else {
k.scale = scale;
k.deadzone = deadzone;
k.setIndex(index);
}
runOutput(0);
}
}

View file

@ -13,9 +13,21 @@ class SetLocationNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var object: Object = inputs[1].get(); var object: Object = inputs[1].get();
var vec: Vec4 = inputs[2].get(); var vec: Vec4 = inputs[2].get();
var relative: Bool = inputs[3].get();
if (object == null || vec == null) return; if (object == null || vec == null) return;
if (!relative && object.parent != null) {
var loc = vec.clone();
loc.sub(object.parent.transform.world.getLoc()); // Remove parent location influence
// Convert vec to parent local space
var dotX = loc.dot(object.parent.transform.right());
var dotY = loc.dot(object.parent.transform.look());
var dotZ = loc.dot(object.parent.transform.up());
vec.set(dotX, dotY, dotZ);
}
object.transform.loc.setFrom(vec); object.transform.loc.setFrom(vec);
object.transform.buildMatrix(); object.transform.buildMatrix();
@ -26,4 +38,4 @@ class SetLocationNode extends LogicNode {
runOutput(0); runOutput(0);
} }
} }

View file

@ -1,40 +1,40 @@
package armory.logicnode; package armory.logicnode;
import iron.Scene;
import iron.data.MaterialData; import iron.data.MaterialData;
import iron.object.Object; import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialImageParamNode extends LogicNode { class SetMaterialImageParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, kha.Image>>();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalTextureLinks.push(textureLink);
}
} }
override function run(from: Int) { override function run(from: Int) {
var mat = inputs[1].get(); var perObject: Null<Bool>;
if (mat == null) return;
var entry = map.get(mat); var object = inputs[1].get();
if (entry == null) { if(object == null) return;
entry = new Map();
map.set(mat, entry); perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Texture);
object = Scene.active.root;
} }
iron.data.Data.getImage(inputs[3].get(), function(image: kha.Image) { var img = inputs[5].get();
entry.set(inputs[2].get(), image); // Node name, value if(img == null) return;
iron.data.Data.getImage(img, function(image: kha.Image) {
UniformsManager.setTextureValue(mat, object, inputs[4].get(), image);
}); });
runOutput(0); runOutput(0);
} }
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
} }

View file

@ -1,38 +1,35 @@
package armory.logicnode; package armory.logicnode;
import iron.Scene;
import iron.math.Vec4; import iron.math.Vec4;
import iron.data.MaterialData; import iron.data.MaterialData;
import iron.object.Object; import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialRgbParamNode extends LogicNode { class SetMaterialRgbParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, Vec4>>();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalVec3Links.push(vec3Link);
}
} }
override function run(from: Int) { override function run(from: Int) {
var mat = inputs[1].get(); var perObject: Null<Bool>;
if (mat == null) return;
var entry = map.get(mat); var object = inputs[1].get();
if (entry == null) { if(object == null) return;
entry = new Map();
map.set(mat, entry); perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Vector);
object = Scene.active.root;
} }
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
UniformsManager.setVec3Value(mat, object, inputs[4].get(), inputs[5].get());
runOutput(0); runOutput(0);
} }
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
} }

View file

@ -1,37 +1,35 @@
package armory.logicnode; package armory.logicnode;
import iron.Scene;
import iron.data.MaterialData; import iron.data.MaterialData;
import iron.object.Object; import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialValueParamNode extends LogicNode { class SetMaterialValueParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, Null<kha.FastFloat>>>();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalFloatLinks.push(floatLink);
}
} }
override function run(from: Int) { override function run(from: Int) {
var mat = inputs[1].get(); var perObject: Null<Bool>;
if (mat == null) return;
var entry = map.get(mat); var object = inputs[1].get();
if (entry == null) { if(object == null) return;
entry = new Map();
map.set(mat, entry); perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Float);
object = Scene.active.root;
} }
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
UniformsManager.setFloatValue(mat, object, inputs[4].get(), inputs[5].get());
runOutput(0); runOutput(0);
} }
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
} }

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.object.MeshObject;
class SetObjectShapeKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
#if arm_morph_target
var object: Dynamic = inputs[1].get();
var shapeKey: String = inputs[2].get();
var value: Dynamic = inputs[3].get();
assert(Error, object != null, "Object should not be null");
var morph = cast(object, MeshObject).morphTarget;
assert(Error, morph != null, "Object does not have shape keys");
morph.setMorphValue(shapeKey, value);
#end
runOutput(0);
}
}

View file

@ -14,8 +14,9 @@ class SetParentNode extends LogicNode {
var parent: Object; var parent: Object;
var isUnparent = false; var isUnparent = false;
if (Std.is(inputs[2].node, ObjectNode)) {
var parentNode = cast(inputs[2].node, ObjectNode); if (Std.isOfType(inputs[2].fromNode, ObjectNode)) {
var parentNode = cast(inputs[2].fromNode, ObjectNode);
isUnparent = parentNode.objectName == ""; isUnparent = parentNode.objectName == "";
} }
if (isUnparent) parent = iron.Scene.active.root; if (isUnparent) parent = iron.Scene.active.root;
@ -24,7 +25,7 @@ class SetParentNode extends LogicNode {
if (object == null || parent == null || object.parent == parent) return; if (object == null || parent == null || object.parent == parent) return;
object.parent.removeChild(object, isUnparent); // keepTransform object.parent.removeChild(object, isUnparent); // keepTransform
#if arm_physics #if arm_physics
var rigidBody = object.getTrait(RigidBody); var rigidBody = object.getTrait(RigidBody);
if (rigidBody != null) rigidBody.setActivationState(0); if (rigidBody != null) rigidBody.setActivationState(0);

View file

@ -1,13 +1,13 @@
package armory.logicnode; package armory.logicnode;
import iron.object.Object; import iron.object.Object;
import iron.math.Quat; import iron.math.Quat;
import iron.math.Vec4;
import armory.trait.physics.RigidBody; import armory.trait.physics.RigidBody;
class SetRotationNode extends LogicNode { class SetRotationNode extends LogicNode {
public var property0: String; public var property0: String; // UNUSED
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
@ -16,27 +16,13 @@ class SetRotationNode extends LogicNode {
override function run(from: Int) { override function run(from: Int) {
var object: Object = inputs[1].get(); var object: Object = inputs[1].get();
if (object == null) return; if (object == null) return;
var vec: Vec4 = inputs[2].get(); var q: Quat = inputs[2].get();
if (vec == null) return; if (q == null) return;
var w: Float = inputs[3].get();
switch (property0) { q.normalize();
case "Euler Angles": object.transform.rot = q;
object.transform.rot.fromEuler(vec.x, vec.y, vec.z);
case "Angle Axies (Degrees)" | "Angle Axies (Radians)":
var angle: Float = w;
if (property0 == "Angle Axies (Degrees)") {
angle = angle * (Math.PI / 180);
}
var angleSin = Math.sin(angle / 2);
vec = vec.normalize();
var angleCos = Math.cos(angle / 2);
object.transform.rot = new Quat(vec.x * angleSin, vec.y * angleSin, vec.z * angleSin, angleCos);
case "Quaternion":
object.transform.rot = new Quat(vec.x, vec.y, vec.z, w);
object.transform.rot.normalize();
}
object.transform.buildMatrix(); object.transform.buildMatrix();
#if arm_physics #if arm_physics
var rigidBody = object.getTrait(RigidBody); var rigidBody = object.getTrait(RigidBody);
if (rigidBody != null) { if (rigidBody != null) {

View file

@ -10,7 +10,7 @@ class SetTraitPausedNode extends LogicNode {
var trait: Dynamic = inputs[1].get(); var trait: Dynamic = inputs[1].get();
var paused: Bool = inputs[2].get(); var paused: Bool = inputs[2].get();
if (trait == null || !Std.is(trait, LogicTree)) return; if (trait == null || !Std.isOfType(trait, LogicTree)) return;
paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume(); paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume();

View file

@ -7,9 +7,9 @@ import iron.math.Quat;
class TransformNode extends LogicNode { class TransformNode extends LogicNode {
var value: Mat4 = Mat4.identity(); var value: Mat4 = Mat4.identity();
static var q = new Quat(); var q = new Quat();
static var v1 = new Vec4(); var v1 = new Vec4();
static var v2 = new Vec4(); var v2 = new Vec4();
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
super(tree); super(tree);
@ -17,18 +17,21 @@ class TransformNode extends LogicNode {
override function get(from: Int): Dynamic { override function get(from: Int): Dynamic {
var loc: Vec4 = inputs[0].get(); var loc: Vec4 = inputs[0].get();
var rot: Vec4 = inputs[1].get(); var rot: Quat = new Quat().setFrom(inputs[1].get());
rot.normalize();
var scale: Vec4 = inputs[2].get(); var scale: Vec4 = inputs[2].get();
if (loc == null && rot == null && scale == null) return this.value;
if (loc == null || rot == null || scale == null) return null; if (loc == null || rot == null || scale == null) return null;
q.fromEuler(rot.x, rot.y, rot.z); this.value.compose(loc, rot, scale);
value.compose(loc, q, scale); return this.value;
return value;
} }
override function set(value: Dynamic) { override function set(value: Dynamic) {
cast(value, Mat4).decompose(v1, q, v2); if (inputs.length>0){
inputs[0].set(v1); cast(value, Mat4).decompose(v1, q, v2);
inputs[1].set(q.getEuler()); inputs[0].set(v1);
inputs[2].set(v2); inputs[1].set(q);
inputs[2].set(v2);
}else this.value = value;
} }
} }

View file

@ -0,0 +1,114 @@
package armory.logicnode;
import iron.system.Tween;
class TweenFloatNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Float = 0.0;
public var toValue:Float = 1.0;
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue = inputs[2].get();
toValue = inputs[3].get();
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -0,0 +1,116 @@
package armory.logicnode;
import iron.math.Quat;
import iron.system.Tween;
class TweenRotationNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Quat = new Quat();
public var toValue:Quat = new Quat();
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue.setFrom(inputs[2].get());
toValue.setFrom(inputs[3].get());
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -0,0 +1,116 @@
package armory.logicnode;
import iron.math.Vec4;
import iron.system.Tween;
class TweenVectorNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Vec4 = new Vec4();
public var toValue:Vec4 = new Vec4();
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue.setFrom(inputs[2].get());
toValue.setFrom(inputs[3].get());
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -7,7 +7,7 @@ class VectorMixNode extends LogicNode {
public var property0: String; // Type public var property0: String; // Type
public var property1: String; // Ease public var property1: String; // Ease
public var property2: String; // Clamp public var property2: Bool; // Clamp
var v = new Vec4(); var v = new Vec4();
@ -57,7 +57,7 @@ class VectorMixNode extends LogicNode {
v.y = v1.y + (v2.y - v1.y) * f; v.y = v1.y + (v2.y - v1.y) * f;
v.z = v1.z + (v2.z - v1.z) * f; v.z = v1.z + (v2.z - v1.z) * f;
if (property2 == "true") v.clamp(0, 1); if (property2) v.clamp(0, 1);
return v; return v;
} }
} }

View file

@ -10,9 +10,9 @@ class VectorNode extends LogicNode {
super(tree); super(tree);
if (x != null) { if (x != null) {
addInput(new FloatNode(tree, x), 0); LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
addInput(new FloatNode(tree, y), 0); LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
addInput(new FloatNode(tree, z), 0); LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
} }
} }

View file

@ -3,8 +3,6 @@ package armory.logicnode;
import iron.object.Object; import iron.object.Object;
import iron.math.Vec4; import iron.math.Vec4;
using armory.object.TransformExtension;
class VectorToObjectOrientationNode extends LogicNode { class VectorToObjectOrientationNode extends LogicNode {
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
@ -18,7 +16,7 @@ class VectorToObjectOrientationNode extends LogicNode {
if (object == null || vec == null) return null; if (object == null || vec == null) return null;
return object.transform.worldVecToOrientation(vec); return vec.applyQuat(object.transform.rot);
} }
} }

View file

@ -1,6 +1,8 @@
package armory.logicnode; package armory.logicnode;
import iron.math.Vec4; import iron.math.Vec4;
import iron.math.Vec2;
import iron.App;
class WorldToScreenSpaceNode extends LogicNode { class WorldToScreenSpaceNode extends LogicNode {
@ -19,7 +21,10 @@ class WorldToScreenSpaceNode extends LogicNode {
v.setFrom(v1); v.setFrom(v1);
v.applyproj(cam.V); v.applyproj(cam.V);
v.applyproj(cam.P); v.applyproj(cam.P);
var w = App.w();
var h = App.h();
return v; return new Vec2((v.x + 1) * 0.5 * w, (-v.y + 1) * 0.5 * h);
} }
} }

View file

@ -3,8 +3,6 @@ package armory.logicnode;
import iron.math.Vec4; import iron.math.Vec4;
import iron.object.Object; import iron.object.Object;
using armory.object.TransformExtension;
class WorldVectorToLocalSpaceNode extends LogicNode { class WorldVectorToLocalSpaceNode extends LogicNode {
public function new(tree: LogicTree) { public function new(tree: LogicTree) {
@ -17,7 +15,8 @@ class WorldVectorToLocalSpaceNode extends LogicNode {
if (object == null || worldVec == null) return null; if (object == null || worldVec == null) return null;
var localVec: Vec4 = new Vec4(); var localVec = new Vec4();
localVec.sub(object.transform.world.getLoc());
localVec.x = worldVec.dot(object.transform.right()); localVec.x = worldVec.dot(object.transform.right());
localVec.y = worldVec.dot(object.transform.look()); localVec.y = worldVec.dot(object.transform.look());

View file

@ -34,6 +34,14 @@ class Uniforms {
return armory.data.ConstData.ltcMagTex; return armory.data.ConstData.ltcMagTex;
} }
#end #end
#if arm_morph_target
case "_morphDataPos": {
return cast(object, iron.object.MeshObject).morphTarget.morphDataPos;
}
case "_morphDataNor": {
return cast(object, iron.object.MeshObject).morphTarget.morphDataNor;
}
#end
} }
var target = iron.RenderPath.active.renderTargets.get(link.endsWith("_depth") ? link.substr(0, link.length - 6) : link); var target = iron.RenderPath.active.renderTargets.get(link.endsWith("_depth") ? link.substr(0, link.length - 6) : link);

View file

@ -715,7 +715,9 @@ class ShadowMapAtlas {
public static inline function getMaxAtlasSize(type: String): Int { public static inline function getMaxAtlasSize(type: String): Int {
#if arm_shadowmap_atlas_single_map #if arm_shadowmap_atlas_single_map
#if (rp_shadowmap_atlas_max_size == 1024) #if (rp_shadowmap_atlas_max_size == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size == 1024)
return 1024; return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048) #elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048; return 2048;
@ -742,7 +744,9 @@ class ShadowMapAtlas {
#end #end
} }
case "spot": { case "spot": {
#if (rp_shadowmap_atlas_max_size_spot == 1024) #if (rp_shadowmap_atlas_max_size_spot == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size_spot == 1024)
return 1024; return 1024;
#elseif (rp_shadowmap_atlas_max_size_spot == 2048) #elseif (rp_shadowmap_atlas_max_size_spot == 2048)
return 2048; return 2048;
@ -755,7 +759,9 @@ class ShadowMapAtlas {
#end #end
} }
case "sun": { case "sun": {
#if (rp_shadowmap_atlas_max_size_sun == 1024) #if (rp_shadowmap_atlas_max_size_sun == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size_sun == 1024)
return 1024; return 1024;
#elseif (rp_shadowmap_atlas_max_size_sun == 2048) #elseif (rp_shadowmap_atlas_max_size_sun == 2048)
return 2048; return 2048;
@ -768,7 +774,9 @@ class ShadowMapAtlas {
#end #end
} }
default: { default: {
#if (rp_shadowmap_atlas_max_size == 1024) #if (rp_shadowmap_atlas_max_size == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size == 1024)
return 1024; return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048) #elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048; return 2048;

View file

@ -80,11 +80,24 @@ class NishitaData {
**/ **/
public static var radiusPlanet = 6360000; public static var radiusPlanet = 6360000;
/** Rayleigh scattering coefficient. **/
public static var rayleighCoeff = new Vec3(5.5e-6, 13.0e-6, 22.4e-6);
/** Rayleigh scattering scale parameter. **/ /** Rayleigh scattering scale parameter. **/
public static var rayleighScale = 8e3; public static var rayleighScale = 8e3;
/** Mie scattering coefficient. **/
public static var mieCoeff = 2e-5;
/** Mie scattering scale parameter. **/ /** Mie scattering scale parameter. **/
public static var mieScale = 1.2e3; public static var mieScale = 1.2e3;
/** Ozone scattering coefficient. **/
// The ozone absorption coefficients are taken from Cycles code.
// Because Cycles calculates 21 wavelengths, we use the coefficients
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
// Precalculating values by simulating Blender's spec_to_xyz() function
// to include all 21 wavelengths gave unrealistic results.
public static var ozoneCoeff = new Vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914);
public function new() {} public function new() {}
/** Approximates the density of ozone for a given sample height. **/ /** Approximates the density of ozone for a given sample height. **/
@ -185,6 +198,29 @@ class NishitaData {
jTime += jStepSize; jTime += jStepSize;
} }
return jODepth.mult(jStepSize); jODepth.mult(jStepSize);
// Precalculate a part of the secondary attenuation.
// For one variable (e.g. x) in the vector, the formula is as follows:
//
// attn.x = exp(-(coeffX * (firstOpticalDepth.x + secondOpticalDepth.x)))
//
// We can split that up via:
//
// attn.x = exp(-(coeffX * firstOpticalDepth.x + coeffX * secondOpticalDepth.x))
// = exp(-(coeffX * firstOpticalDepth.x)) * exp(-(coeffX * secondOpticalDepth.x))
//
// The first factor of the resulting multiplication is calculated in the
// shader, but we can already precalculate the second one. As a side
// effect this keeps the range of the LUT values small because we don't
// store the optical depth but the attenuation.
var jAttenuation = new Vec3();
var mie = mieCoeff * jODepth.y;
jAttenuation.addf(mie, mie, mie);
jAttenuation.add(rayleighCoeff.clone().mult(jODepth.x));
jAttenuation.add(ozoneCoeff.clone().mult(jODepth.z));
jAttenuation.exp(jAttenuation.mult(-1));
return jAttenuation;
} }
} }

View file

@ -0,0 +1,131 @@
package armory.system;
import haxe.Exception;
import haxe.PosInfos;
import haxe.exceptions.PosException;
import haxe.macro.Context;
import haxe.macro.Expr;
using haxe.macro.ExprTools;
class Assert {
/**
Checks whether the given expression evaluates to true. If this is not
the case, an `ArmAssertionException` is thrown or a warning is printed
(depending on the assertion level).
The assert level describes the severity of the assertion. If the
severity is lower than the level stored in the `arm_assert_level` flag,
the assertion is omitted from the code so that it doesn't decrease the
runtime performance.
@param level The severity of this assertion.
@param condition The conditional expression to test.
@param message Optional message to display when the assertion fails.
@see `AssertLevel`
**/
macro public static function assert(level: ExprOf<AssertLevel>, condition: ExprOf<Bool>, ?message: ExprOf<String>): Expr {
final levelVal: AssertLevel = AssertLevel.fromExpr(level);
final assertThreshold = AssertLevel.fromString(Context.definedValue("arm_assert_level"));
if (levelVal < assertThreshold) {
return macro {};
}
switch (levelVal) {
case Warning:
return macro {
if (!$condition) {
@:pos(condition.pos)
trace(@:privateAccess armory.system.Assert.ArmAssertionException.formatMessage($v{condition.toString()}, ${message}));
}
}
case Error:
return macro {
if (!$condition) {
#if arm_assert_quit kha.System.stop(); #end
@:pos(condition.pos)
@:privateAccess armory.system.Assert.throwAssertionError($v{condition.toString()}, ${message});
}
}
default:
throw new Exception('Unsupported assert level: $levelVal');
}
}
/**
Helper function to prevent Haxe "bug" that actually throws an error
even when using `macro throw` (inlining this method also does not work).
**/
static function throwAssertionError(exprString: String, message: String, ?pos: PosInfos) {
throw new ArmAssertionException(exprString, message, pos);
}
}
/**
Exception that is thrown when an assertion fails.
@see `Assert`
**/
class ArmAssertionException extends PosException {
/**
@param exprString The string representation of the failed assert condition.
@param message Custom error message, use `null` to omit this.
**/
public inline function new(exprString: String, message: Null<String>, ?previous: Exception, ?pos: Null<PosInfos>) {
super('\n${formatMessage(exprString, message)}', previous, pos);
}
static inline function formatMessage(exprString: String, message: Null<String>): String {
final optMsg = message != null ? '\n\tMessage: $message' : "";
return 'Failed assertion:$optMsg\n\tExpression: ($exprString)';
}
}
enum abstract AssertLevel(Int) from Int to Int {
/**
Assertions with this severity don't throw exceptions and only print to
the console.
**/
var Warning: AssertLevel = 0;
/**
Assertions with this severity throw an `ArmAssertionException` if they
fail, and optionally quit the game if the `arm_assert_quit` flag is set.
**/
var Error: AssertLevel = 1;
/**
Completely disable assertions. Don't use this level in `assert()` calls!
**/
var NoAssertions: AssertLevel = 2;
public static function fromExpr(e: ExprOf<AssertLevel>): AssertLevel {
switch (e.expr) {
case EConst(CIdent(v)): return fromString(v);
default: throw new Exception('Unsupported expression: $e');
};
}
/**
Converts a string into an `AssertLevel`, the string must be spelled
exactly as the assert level. `null` defaults to
`AssertLevel.NoAssertions`.
**/
public static function fromString(s: Null<String>): AssertLevel {
return switch (s) {
case "Warning": Warning;
case "Error": Error;
case "NoAssertions" | null: NoAssertions;
default: throw new Exception('Could not convert "$s" to AssertLevel');
}
}
@:op(A < B) static function lt(a: AssertLevel, b: AssertLevel): Bool;
@:op(A > B) static function gt(a: AssertLevel, b: AssertLevel): Bool;
}

View file

@ -4,347 +4,223 @@ import kha.FastFloat;
import iron.system.Input; import iron.system.Input;
class InputMap { class InputMap {
var commands = new Map<String, Null<Array<InputCommand>>>();
static var inputMaps = new Map<String, InputMap>();
public var keys(default, null) = new Array<InputMapKey>();
public var lastKeyPressed(default, null) = "";
public function new() {} public function new() {}
public function addKeyboard(config: String) { public static function getInputMap(inputMap: String): Null<InputMap> {
var command = new KeyboardCommand(); if (inputMaps.exists(inputMap)) {
return addCustomCommand(command, config); return inputMaps[inputMap];
}
return null;
} }
public function addGamepad(config: String) { public static function addInputMap(inputMap: String): InputMap {
var command = new GamepadCommand(); return inputMaps[inputMap] = new InputMap();
return addCustomCommand(command, config);
} }
public function addCustomCommand(command: InputCommand, config: String) { public static function getInputMapKey(inputMap: String, key: String): Null<InputMapKey> {
if (commands[config] == null) commands[config] = new Array<InputCommand>(); if (inputMaps.exists(inputMap)) {
commands[config].push(command); for (k in inputMaps[inputMap].keys) {
return command; if (k.key == key) {
} return k;
} }
class ActionMap extends InputMap {
public inline function started(config: String) {
var started = false;
for (c in commands[config]) {
if (c.started()) {
started = true;
break;
} }
} }
return started; return null;
} }
public inline function released(config: String) { public static function removeInputMapKey(inputMap: String, key: String): Bool {
var released = false; if (inputMaps.exists(inputMap)) {
var i = inputMaps[inputMap];
for (c in commands[config]) { for (k in i.keys) {
if (c.released()) { if (k.key == key) {
released = true; return i.removeKey(k);
break; }
} }
} }
return released; return false;
} }
}
class AxisMap extends InputMap { public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey {
var scale: FastFloat = 1.0; return addKey(new KeyboardKey(key, scale));
}
public inline function getAxis(config: String) { public function addMouse(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
var axis = 0.0; return addKey(new MouseKey(key, scale, deadzone));
}
for (c in commands[config]) { public function addGamepad(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
var tempAxis = c.getAxis(); return addKey(new GamepadKey(key, scale, deadzone));
}
if (tempAxis != 0.0 && tempAxis != axis) { public function addKey(key: InputMapKey): InputMapKey {
axis += tempAxis; keys.push(key);
scale = c.getScale(); return key;
} }
public function removeKey(key: InputMapKey): Bool {
return keys.remove(key);
}
public function value(): FastFloat {
var v = 0.0;
for (k in keys) {
v += k.value();
} }
return axis; return v;
}
public inline function getScale() {
return scale;
}
}
class InputCommand {
var keys = new Array<String>();
var modifiers = new Array<String>();
var displacementKeys = new Array<String>();
var displacementModifiers = new Array<String>();
var deadzone: FastFloat = 0.0;
var scale: FastFloat = 1.0;
public function new() {}
public function setKeys(keys: Array<String>) {
return this.keys = keys;
}
public function setMods(modifiers: Array<String>) {
return this.modifiers = modifiers;
}
public function setDisplacementKeys(keys: Array<String>) {
return displacementKeys = keys;
}
public function setDisplacementMods(modifiers: Array<String>) {
return displacementModifiers = modifiers;
}
public function setDeadzone(deadzone: FastFloat) {
return this.deadzone = deadzone;
}
public function setScale(scale: FastFloat) {
return this.scale = scale;
}
public function getScale() {
return scale;
} }
public function started() { public function started() {
for (k in keys) {
if (k.started()) {
lastKeyPressed = k.key;
return true;
}
}
return false; return false;
} }
public function released() { public function released() {
for (k in keys) {
if (k.released()) {
lastKeyPressed = k.key;
return true;
}
}
return false;
}
}
class InputMapKey {
public var key: String;
public var scale: FastFloat;
public var deadzone: FastFloat;
public function new(key: String, scale = 1.0, deadzone = 0.0) {
this.key = key.toLowerCase();
this.scale = scale;
this.deadzone = deadzone;
}
public function started(): Bool {
return false; return false;
} }
public function getAxis(): FastFloat { public function released(): Bool {
return false;
}
public function value(): FastFloat {
return 0.0; return 0.0;
} }
}
class KeyboardCommand extends InputCommand { public function setIndex(index: Int) {}
var keyboard = Input.getKeyboard();
var mouse = Input.getMouse();
public inline override function started() { function evalDeadzone(value: FastFloat): FastFloat {
for (k in keys) { var v = 0.0;
if (keyboard.started(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) { if (value > deadzone) {
if (!mouse.down(m)) return false; v = value - deadzone;
}
return true; } else if (value < -deadzone) {
} v = value + deadzone;
} }
for (k in displacementKeys) { return v * scale;
if (mouse.started(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
return true;
}
}
return false;
} }
public inline override function released() { function evalPressure(value: FastFloat): FastFloat {
for (k in keys) { var v = value - deadzone;
if (keyboard.released(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) { if (v > 0.0) {
if (!mouse.down(m)) return false; v /= (1.0 - deadzone);
}
return true; } else {
} v = 0.0;
} }
for (k in displacementKeys) { return v;
if (mouse.released(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
return true;
}
}
return false;
}
public inline override function getAxis() {
var axis = 0.0;
var movementX = mouse.movementX;
var movementY = mouse.movementY;
var wheelDelta = mouse.wheelDelta;
for (k in keys) {
if (keyboard.down(k)) {
axis++;
break;
}
}
for (m in modifiers) {
if (keyboard.down(m)) {
axis --;
break;
}
}
for (k in displacementKeys) {
switch (k) {
case "moved x": if (movementX > deadzone) axis++;
case "moved y": if (movementY > deadzone) axis--;
case "wheel": if (wheelDelta < -deadzone) axis++;
case "movement x": if (movementX > deadzone) return movementX - deadzone;
case "movement y": if (movementY > deadzone) return movementY - deadzone;
default: {
if (mouse.down(k)) {
axis ++;
break;
}
}
}
}
for (m in displacementModifiers) {
switch (m) {
case "moved x": if (movementX < -deadzone) axis--;
case "moved y": if (movementY < -deadzone) axis++;
case "wheel": if (wheelDelta > deadzone) axis--;
case "movement x": if (movementX < -deadzone) return movementX + deadzone;
case "movement y": if (movementY < -deadzone) return movementY + deadzone;
default: {
if (mouse.down(m)) {
axis --;
break;
}
}
}
}
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
} }
} }
class GamepadCommand extends InputCommand { class KeyboardKey extends InputMapKey {
var gamepad = Input.getGamepad(0);
var kb = Input.getKeyboard();
public inline override function started() { public inline override function started() {
for (k in keys) { return kb.started(key);
if (gamepad.started(k)) {
for (m in modifiers) {
if (gamepad.down(m) < deadzone) return false;
}
return true;
}
}
return false;
} }
public inline override function released() { public inline override function released() {
for (k in keys) { return kb.released(key);
if (gamepad.released(k)) {
for (m in modifiers) {
if (gamepad.down(m) < deadzone) return false;
}
return true;
}
}
return false;
} }
public inline override function getAxis() { public inline override function value(): FastFloat {
var axis = 0.0; return kb.down(key) ? scale : 0.0;
var rsMovementX = gamepad.rightStick.movementX;
var rsMovementY = gamepad.rightStick.movementY;
var lsMovementX = gamepad.leftStick.movementX;
var lsMovementY = gamepad.leftStick.movementY;
var rtPressure = gamepad.down("r2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
var ltPressure = gamepad.down("l2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
for (k in keys) {
switch(k) {
case "rtPressure": axis += rtPressure;
case "ltPressure": axis += ltPressure;
default: {
if (gamepad.down(k) > deadzone) {
axis++;
break;
}
}
}
}
for (m in modifiers) {
switch (m) {
case "rtPressure": axis -= rtPressure;
case "ltPressure": axis -= ltPressure;
default: {
if (gamepad.down(m) > deadzone) {
axis--;
break;
}
}
}
}
for (k in displacementKeys) {
switch(k) {
case "rs moved x": if (rsMovementX > deadzone) axis++;
case "rs moved y": if (rsMovementY > deadzone) axis++;
case "ls moved x": if (lsMovementX > deadzone) axis++;
case "ls moved y": if (lsMovementY > deadzone) axis++;
case "rs movement x": if (rsMovementX > deadzone) return rsMovementX - deadzone;
case "rs movement y": if (rsMovementY > deadzone) return rsMovementY - deadzone;
case "ls movement x": if (lsMovementX > deadzone) return lsMovementX - deadzone;
case "ls movement y": if (lsMovementY > deadzone) return lsMovementY - deadzone;
}
}
for (m in displacementModifiers) {
switch (m) {
case "rs moved x": if (rsMovementX < -deadzone) axis--;
case "rs moved y": if (rsMovementY < -deadzone) axis--;
case "ls moved x": if (lsMovementX < -deadzone) axis--;
case "ls moved y": if (lsMovementY < -deadzone) axis--;
case "rs movement x": if (rsMovementX < -deadzone) return rsMovementX + deadzone;
case "rs movement y": if (rsMovementY < -deadzone) return rsMovementY + deadzone;
case "ls movement x": if (lsMovementX < -deadzone) return lsMovementX + deadzone;
case "ls movement y": if (lsMovementY < -deadzone) return lsMovementY + deadzone;
}
}
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
} }
} }
class MouseKey extends InputMapKey {
var m = Input.getMouse();
public inline override function started() {
return m.started(key);
}
public inline override function released() {
return m.released(key);
}
public override function value(): FastFloat {
return switch (key) {
case "movement x": evalDeadzone(m.movementX);
case "movement y": evalDeadzone(m.movementY);
case "wheel": evalDeadzone(m.wheelDelta);
default: m.down(key) ? scale : 0.0;
}
}
}
class GamepadKey extends InputMapKey {
var g = Input.getGamepad();
public inline override function started() {
return g.started(key);
}
public inline override function released() {
return g.released(key);
}
public override function value(): FastFloat {
return switch(key) {
case "ls movement x": evalDeadzone(g.leftStick.movementX);
case "ls movement y": evalDeadzone(g.leftStick.movementY);
case "rs movement x": evalDeadzone(g.rightStick.movementX);
case "rs movement y": evalDeadzone(g.rightStick.movementY);
case "lt pressure": evalDeadzone(evalPressure(g.down("l2")));
case "rt pressure": evalDeadzone(evalPressure(g.down("r2")));
default: evalDeadzone(g.down(key));
}
}
public override function setIndex(index: Int) {
g = Input.getGamepad(index);
}
}

View file

@ -94,6 +94,10 @@ class Logic {
var v = createClassInstance(node.type, [tree]); var v = createClassInstance(node.type, [tree]);
nodeMap.set(name, v); nodeMap.set(name, v);
#if arm_patch
tree.nodes.set(name, v);
#end
// Properties // Properties
for (i in 0...5) { for (i in 0...5) {
for (b in node.buttons) { for (b in node.buttons) {
@ -103,9 +107,13 @@ class Logic {
} }
} }
@:privateAccess v.preallocInputs(node.inputs.length);
@:privateAccess v.preallocOutputs(node.outputs.length);
// Create inputs // Create inputs
var inp_node: armory.logicnode.LogicNode = null; var inp_node: armory.logicnode.LogicNode = null;
var inp_from = 0; var inp_from = 0;
var from_type: String;
for (i in 0...node.inputs.length) { for (i in 0...node.inputs.length) {
var inp = node.inputs[i]; var inp = node.inputs[i];
// Is linked - find node // Is linked - find node
@ -117,6 +125,7 @@ class Logic {
for (i in 0...n.outputs.length) { for (i in 0...n.outputs.length) {
if (n.outputs[i] == socket) { if (n.outputs[i] == socket) {
inp_from = i; inp_from = i;
from_type = socket.type;
break; break;
} }
} }
@ -124,27 +133,33 @@ class Logic {
else { // Not linked - create node with default values else { // Not linked - create node with default values
inp_node = build_default_node(inp); inp_node = build_default_node(inp);
inp_from = 0; inp_from = 0;
from_type = inp.type;
} }
// Add input // Add input
v.addInput(inp_node, inp_from); var link = LogicNode.addLink(inp_node, v, inp_from, i);
#if arm_patch
link.fromType = from_type;
link.toType = inp.type;
link.toValue = getSocketDefaultValue(inp);
#end
} }
// Create outputs // Create outputs
for (out in node.outputs) { for (i in 0...node.outputs.length) {
var outNodes: Array<armory.logicnode.LogicNode> = []; var out = node.outputs[i];
var ls = getOutputLinks(out); var ls = getOutputLinks(out);
if (ls != null && ls.length > 0) {
for (l in ls) { // Linked outputs are already handled after iterating over inputs
var n = getNode(l.to_id); // above, so only unconnected outputs are handled here
var out_name = build_node(n); if (ls == null || ls.length == 0) {
outNodes.push(nodeMap.get(out_name)); var link = LogicNode.addLink(v, build_default_node(out), i, 0);
}
#if arm_patch
link.fromType = out.type;
link.toType = out.type;
link.toValue = getSocketDefaultValue(out);
#end
} }
else { // Not linked - create node with default values
outNodes.push(build_default_node(out));
}
// Add outputs
v.addOutputs(outNodes);
} }
return name; return name;
@ -212,6 +227,22 @@ class Logic {
return v; return v;
} }
static function getSocketDefaultValue(socket: TNodeSocket): Any {
var v: armory.logicnode.LogicNode = null;
return switch (socket.type) {
case "OBJECT" | "VALUE" | "INT" | "BOOLEAN" | "STRING":
socket.default_value;
case "VECTOR" | "RGB":
socket.default_value == null ? [0, 0, 0] : [socket.default_value[0], socket.default_value[1], socket.default_value[2]];
case "RGBA":
socket.default_value == null ? [0, 0, 0, 1] : [socket.default_value[0], socket.default_value[1], socket.default_value[2], socket.default_value[3]];
default:
null;
}
}
static function createClassInstance(className: String, args: Array<Dynamic>): Dynamic { static function createClassInstance(className: String, args: Array<Dynamic>): Dynamic {
var cname = Type.resolveClass(packageName + "." + className); var cname = Type.resolveClass(packageName + "." + className);
if (cname == null) return null; if (cname == null) return null;

View file

@ -6,6 +6,9 @@ import iron.math.Vec4;
class ArcBall extends Trait { class ArcBall extends Trait {
@prop
public var axis = new Vec4(0, 0, 1);
public function new() { public function new() {
super(); super();
@ -17,10 +20,8 @@ class ArcBall extends Trait {
var mouse = Input.getMouse(); var mouse = Input.getMouse();
if (mouse.down()) { if (mouse.down()) {
object.transform.rotate(new Vec4(0, 0, 1), -mouse.movementX / 100); object.transform.rotate(axis, -mouse.movementX / 100);
object.transform.buildMatrix();
object.transform.rotate(object.transform.world.right(), -mouse.movementY / 100); object.transform.rotate(object.transform.world.right(), -mouse.movementY / 100);
object.transform.buildMatrix();
} }
} }
} }

View file

@ -33,7 +33,7 @@ class FollowCamera extends iron.Trait {
trace("FollowCamera error, unable to set target object"); trace("FollowCamera error, unable to set target object");
} }
if (Std.is(object, iron.object.CameraObject)) { if (Std.isOfType(object, iron.object.CameraObject)) {
disabled = true; disabled = true;
trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom."); trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom.");
} }

View file

@ -8,9 +8,9 @@ import iron.system.Tween;
class NavAgent extends Trait { class NavAgent extends Trait {
@prop @prop
var speed: Float = 5; public var speed: Float = 5;
@prop @prop
var turnDuration: Float = 0.4; public var turnDuration: Float = 0.4;
var path: Array<Vec4> = null; var path: Array<Vec4> = null;
var index = 0; var index = 0;

View file

@ -2,6 +2,7 @@ package armory.trait;
import iron.Trait; import iron.Trait;
import iron.system.Input; import iron.system.Input;
import iron.math.Vec3;
import iron.math.Vec4; import iron.math.Vec4;
import iron.math.Mat4; import iron.math.Mat4;
import iron.math.RayCaster; import iron.math.RayCaster;
@ -14,6 +15,11 @@ class PhysicsDrag extends Trait {
public function new() { super(); } public function new() { super(); }
#else #else
@prop public var linearLowerLimit = new Vec3(0,0,0);
@prop public var linearUpperLimit = new Vec3(0,0,0);
@prop public var angularLowerLimit = new Vec3(-10,-10,-10);
@prop public var angularUpperLimit = new Vec3(10,10,10);
var pickConstraint: bullet.Bt.Generic6DofConstraint = null; var pickConstraint: bullet.Bt.Generic6DofConstraint = null;
var pickDist: Float; var pickDist: Float;
var pickedBody: RigidBody = null; var pickedBody: RigidBody = null;
@ -56,10 +62,10 @@ class PhysicsDrag extends Trait {
tr.setOrigin(localPivot); tr.setOrigin(localPivot);
pickConstraint = new bullet.Bt.Generic6DofConstraint(b.body, tr, false); pickConstraint = new bullet.Bt.Generic6DofConstraint(b.body, tr, false);
pickConstraint.setLinearLowerLimit(new bullet.Bt.Vector3(0, 0, 0)); pickConstraint.setLinearLowerLimit(new bullet.Bt.Vector3(linearLowerLimit.x, linearLowerLimit.y, linearLowerLimit.z));
pickConstraint.setLinearUpperLimit(new bullet.Bt.Vector3(0, 0, 0)); pickConstraint.setLinearUpperLimit(new bullet.Bt.Vector3(linearUpperLimit.x, linearUpperLimit.y, linearUpperLimit.z));
pickConstraint.setAngularLowerLimit(new bullet.Bt.Vector3(-10, -10, -10)); pickConstraint.setAngularLowerLimit(new bullet.Bt.Vector3(angularLowerLimit.x, angularLowerLimit.y, angularLowerLimit.z));
pickConstraint.setAngularUpperLimit(new bullet.Bt.Vector3(10, 10, 10)); pickConstraint.setAngularUpperLimit(new bullet.Bt.Vector3(angularUpperLimit.x, angularUpperLimit.y, angularUpperLimit.z));
physics.world.addConstraint(pickConstraint, false); physics.world.addConstraint(pickConstraint, false);
/*pickConstraint.setParam(4, 0.8, 0); /*pickConstraint.setParam(4, 0.8, 0);

View file

@ -11,6 +11,7 @@ class Bridge {
public static var Input = iron.system.Input; public static var Input = iron.system.Input;
public static var Object = iron.object.Object; public static var Object = iron.object.Object;
public static var Data = iron.data.Data; public static var Data = iron.data.Data;
public static var Vec4 = iron.math.Vec4;
public static function log(s: String) { trace(s); }; public static function log(s: String) { trace(s); };
} }

View file

@ -29,7 +29,7 @@ class CanvasScript extends Trait {
iron.data.Data.getBlob(canvasName + ".json", function(blob: kha.Blob) { iron.data.Data.getBlob(canvasName + ".json", function(blob: kha.Blob) {
iron.data.Data.getBlob("_themes.json", function(tBlob: kha.Blob) { iron.data.Data.getBlob("_themes.json", function(tBlob: kha.Blob) {
if (tBlob.get_length() != 0) { if (@:privateAccess tBlob.get_length() != 0) {
Canvas.themes = haxe.Json.parse(tBlob.toString()); Canvas.themes = haxe.Json.parse(tBlob.toString());
} }
else { else {
@ -40,20 +40,27 @@ class CanvasScript extends Trait {
Canvas.themes.push(armory.ui.Themes.light); Canvas.themes.push(armory.ui.Themes.light);
} }
iron.data.Data.getFont(font, function(f: kha.Font) { iron.data.Data.getFont(font, function(defaultFont: kha.Font) {
var c: TCanvas = haxe.Json.parse(blob.toString()); var c: TCanvas = haxe.Json.parse(blob.toString());
if (c.theme == null) c.theme = Canvas.themes[0].NAME; if (c.theme == null) c.theme = Canvas.themes[0].NAME;
cui = new Zui({font: f, theme: Canvas.getTheme(c.theme)}); cui = new Zui({font: defaultFont, theme: Canvas.getTheme(c.theme)});
if (c.assets == null || c.assets.length == 0) canvas = c; if (c.assets == null || c.assets.length == 0) canvas = c;
else { // Load canvas assets else { // Load canvas assets
var loaded = 0; var loaded = 0;
for (asset in c.assets) { for (asset in c.assets) {
var file = asset.name; var file = asset.name;
iron.data.Data.getImage(file, function(image: kha.Image) { if (Canvas.isFontAsset(file)) {
Canvas.assetMap.set(asset.id, image); iron.data.Data.getFont(file, function(f: kha.Font) {
if (++loaded >= c.assets.length) canvas = c; Canvas.assetMap.set(asset.id, f);
}); if (++loaded >= c.assets.length) canvas = c;
});
} else {
iron.data.Data.getImage(file, function(image: kha.Image) {
Canvas.assetMap.set(asset.id, image);
if (++loaded >= c.assets.length) canvas = c;
});
}
} }
} }
}); });
@ -107,13 +114,19 @@ class CanvasScript extends Trait {
} }
/** /**
* Set UI scale factor. Set the UI scale factor.
* @param factor Scale factor. **/
*/ public inline function setUiScale(factor: Float) {
public function setUiScale(factor:Float) {
cui.setScale(factor); cui.setScale(factor);
} }
/**
Get the UI scale factor.
**/
public inline function getUiScale(): Float {
return cui.ops.scaleFactor;
}
/** /**
* Set visibility of canvas * Set visibility of canvas
* @param visible Whether canvas should be visible or not * @param visible Whether canvas should be visible or not
@ -137,6 +150,11 @@ class CanvasScript extends Trait {
*/ */
public function setCanvasFontSize(fontSize: Int) { public function setCanvasFontSize(fontSize: Int) {
cui.t.FONT_SIZE = fontSize; cui.t.FONT_SIZE = fontSize;
cui.setScale(cui.ops.scaleFactor);
}
public function getCanvasFontSize(): Int {
return cui.t.FONT_SIZE;
} }
// Contains data // Contains data

View file

@ -25,6 +25,8 @@ class DebugConsole extends Trait {
#else #else
public static var visible = true; public static var visible = true;
public static var traceWithPosition = true;
static var ui: Zui; static var ui: Zui;
var scaleFactor = 1.0; var scaleFactor = 1.0;
@ -75,16 +77,18 @@ class DebugConsole extends Trait {
#end #end
public function new(scaleFactor = 1.0, scaleDebugConsole = 1.0, positionDebugConsole = 2, visibleDebugConsole = 1, public function new(scaleFactor = 1.0, scaleDebugConsole = 1.0, positionDebugConsole = 2, visibleDebugConsole = 1,
keyCodeVisible = kha.input.KeyCode.Tilde, keyCodeScaleIn = kha.input.KeyCode.OpenBracket, keyCodeScaleOut = kha.input.KeyCode.CloseBracket) { traceWithPosition = 1, keyCodeVisible = kha.input.KeyCode.Tilde, keyCodeScaleIn = kha.input.KeyCode.OpenBracket,
keyCodeScaleOut = kha.input.KeyCode.CloseBracket) {
super(); super();
this.scaleFactor = scaleFactor; this.scaleFactor = scaleFactor;
DebugConsole.traceWithPosition = traceWithPosition == 1;
iron.data.Data.getFont("font_default.ttf", function(font: kha.Font) { iron.data.Data.getFont("font_default.ttf", function(font: kha.Font) {
ui = new Zui({scaleFactor: scaleFactor, font: font}); ui = new Zui({scaleFactor: scaleFactor, font: font});
// Set settings // Set settings
setScale(scaleDebugConsole); setScale(scaleDebugConsole);
setVisible(visibleDebugConsole == 1); setVisible(visibleDebugConsole == 1);
switch(positionDebugConsole) { switch (positionDebugConsole) {
case 0: setPosition(PositionStateEnum.LEFT); case 0: setPosition(PositionStateEnum.LEFT);
case 1: setPosition(PositionStateEnum.CENTER); case 1: setPosition(PositionStateEnum.CENTER);
case 2: setPosition(PositionStateEnum.RIGHT); case 2: setPosition(PositionStateEnum.RIGHT);
@ -104,11 +108,11 @@ class DebugConsole extends Trait {
// DebugFloat // DebugFloat
if (key == kha.input.KeyCode.OpenBracket) { if (key == kha.input.KeyCode.OpenBracket) {
debugFloat -= 0.1; debugFloat -= 0.1;
trace("debugFloat = "+ debugFloat); trace("debugFloat = " + debugFloat);
} }
else if (key == kha.input.KeyCode.CloseBracket){ else if (key == kha.input.KeyCode.CloseBracket){
debugFloat += 0.1; debugFloat += 0.1;
trace("debugFloat = "+ debugFloat); trace("debugFloat = " + debugFloat);
} }
// Shortcut - Visible // Shortcut - Visible
if (key == shortcut_visible) visible = !visible; if (key == shortcut_visible) visible = !visible;
@ -170,7 +174,7 @@ class DebugConsole extends Trait {
static var haxeTrace: Dynamic->haxe.PosInfos->Void = null; static var haxeTrace: Dynamic->haxe.PosInfos->Void = null;
static var lastTraces: Array<String> = [""]; static var lastTraces: Array<String> = [""];
static function consoleTrace(v: Dynamic, ?inf: haxe.PosInfos) { static function consoleTrace(v: Dynamic, ?inf: haxe.PosInfos) {
lastTraces.unshift(haxe.Log.formatOutput(v,inf)); lastTraces.unshift(haxe.Log.formatOutput(v, traceWithPosition ? inf : null));
if (lastTraces.length > 10) lastTraces.pop(); if (lastTraces.length > 10) lastTraces.pop();
haxeTrace(v, inf); haxeTrace(v, inf);
} }
@ -186,10 +190,10 @@ class DebugConsole extends Trait {
var wh = iron.App.h(); var wh = iron.App.h();
// Check position // Check position
switch (position_console) { switch (position_console) {
case PositionStateEnum.LEFT: wx = 0; case PositionStateEnum.LEFT: wx = 0;
case PositionStateEnum.CENTER: wx = Math.round(iron.App.w() / 2 - ww / 2); case PositionStateEnum.CENTER: wx = Math.round(iron.App.w() / 2 - ww / 2);
case PositionStateEnum.RIGHT: wx = iron.App.w() - ww; case PositionStateEnum.RIGHT: wx = iron.App.w() - ww;
} }
// var bindG = ui.windowDirty(hwin, wx, wy, ww, wh) || hwin.redraws > 0; // var bindG = ui.windowDirty(hwin, wx, wy, ww, wh) || hwin.redraws > 0;
var bindG = true; var bindG = true;
@ -276,7 +280,7 @@ class DebugConsole extends Trait {
ui.indent(); ui.indent();
if (selectedObject != null) { if (selectedObject != null) {
if (Std.is(selectedObject, iron.object.CameraObject)) { if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
ui.row([1/2, 1/2]); ui.row([1/2, 1/2]);
} }
@ -284,7 +288,7 @@ class DebugConsole extends Trait {
h.selected = selectedObject.visible; h.selected = selectedObject.visible;
selectedObject.visible = ui.check(h, "Visible"); selectedObject.visible = ui.check(h, "Visible");
if (Std.is(selectedObject, iron.object.CameraObject)) { if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
if (ui.button("Set Active Camera")) { if (ui.button("Set Active Camera")) {
iron.Scene.active.camera = cast(selectedObject, iron.object.CameraObject); iron.Scene.active.camera = cast(selectedObject, iron.object.CameraObject);
} }
@ -428,7 +432,7 @@ class DebugConsole extends Trait {
ui.text("This scene has no world data to edit."); ui.text("This scene has no world data to edit.");
} }
} }
else if (Std.is(selectedObject, iron.object.LightObject)) { else if (Std.isOfType(selectedObject, iron.object.LightObject)) {
selectedType = "(Light)"; selectedType = "(Light)";
var light = cast(selectedObject, iron.object.LightObject); var light = cast(selectedObject, iron.object.LightObject);
var lightHandle = Id.handle(); var lightHandle = Id.handle();
@ -439,7 +443,7 @@ class DebugConsole extends Trait {
ui.text("shadow map size: " + light.data.raw.shadowmap_size); ui.text("shadow map size: " + light.data.raw.shadowmap_size);
#end #end
} }
else if (Std.is(selectedObject, iron.object.CameraObject)) { else if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
selectedType = "(Camera)"; selectedType = "(Camera)";
var cam = cast(selectedObject, iron.object.CameraObject); var cam = cast(selectedObject, iron.object.CameraObject);
var fovHandle = Id.handle(); var fovHandle = Id.handle();
@ -594,7 +598,7 @@ class DebugConsole extends Trait {
} }
function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) { function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) {
var color = kha.Color.fromFloats(0.1, 0.1, 0.1); var color: Null<kha.Color> = kha.Color.fromFloats(0.1, 0.1, 0.1);
var borderColor = color; var borderColor = color;
var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95; var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95;
var x = (tile.coordsX / atlas.sizew) * atlasVisualSize; var x = (tile.coordsX / atlas.sizew) * atlasVisualSize;
@ -775,11 +779,23 @@ class DebugConsole extends Trait {
#end #end
if (ui.panel(Id.handle({selected: true}), "Log")) { if (ui.panel(Id.handle({selected: true}), "Log")) {
ui.indent(); ui.indent();
final h = Id.handle();
h.selected = DebugConsole.traceWithPosition;
DebugConsole.traceWithPosition = ui.check(h, "Print With Position");
if (ui.isHovered) ui.tooltip("Whether to prepend the position of print/trace statements to the printed text");
if (ui.button("Clear")) { if (ui.button("Clear")) {
lastTraces[0] = ""; lastTraces[0] = "";
lastTraces.splice(1, lastTraces.length - 1); lastTraces.splice(1, lastTraces.length - 1);
} }
if (ui.isHovered) ui.tooltip("Clear the log output");
final eh = ui.t.ELEMENT_H;
ui.t.ELEMENT_H = ui.fontSize;
for (t in lastTraces) ui.text(t); for (t in lastTraces) ui.text(t);
ui.t.ELEMENT_H = eh;
ui.unindent(); ui.unindent();
} }
} }

View file

@ -1,8 +1,17 @@
package armory.trait.internal; package armory.trait.internal;
import armory.logicnode.LogicNode;
import armory.logicnode.LogicTree;
#if arm_patch @:expose("LivePatch") #end
@:access(armory.logicnode.LogicNode)
@:access(armory.logicnode.LogicNodeLink)
class LivePatch extends iron.Trait { class LivePatch extends iron.Trait {
#if arm_patch #if !arm_patch
public function new() { super(); }
#else
static var patchId = 0; static var patchId = 0;
@ -23,9 +32,164 @@ class LivePatch extends iron.Trait {
}); });
} }
#else public static function patchCreateNodeLink(treeName: String, fromNodeName: String, toNodeName: String, fromIndex: Int, toIndex: Int) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
public function new() { super(); } for (tree in trees) {
var fromNode = tree.nodes[fromNodeName];
var toNode = tree.nodes[toNodeName];
if (fromNode == null || toNode == null) return;
LogicNode.addLink(fromNode, toNode, fromIndex, toIndex);
}
}
public static function patchSetNodeLinks(treeName: String, nodeName: String, inputDatas: Array<Dynamic>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.clearInputs();
node.clearOutputs();
for (inputData in inputDatas) {
var fromNode: LogicNode;
var fromIndex: Int;
if (inputData.isLinked) {
fromNode = tree.nodes[inputData.fromNode];
if (fromNode == null) continue;
fromIndex = inputData.fromIndex;
}
else {
fromNode = LogicNode.createSocketDefaultNode(node.tree, inputData.socketType, inputData.socketValue);
fromIndex = 0;
}
LogicNode.addLink(fromNode, node, fromIndex, inputData.toIndex);
}
for (outputData in outputDatas) {
for (linkData in outputData) {
var toNode: LogicNode;
var toIndex: Int;
if (linkData.isLinked) {
toNode = tree.nodes[linkData.toNode];
if (toNode == null) continue;
toIndex = linkData.toIndex;
}
else {
toNode = LogicNode.createSocketDefaultNode(node.tree, linkData.socketType, linkData.socketValue);
toIndex = 0;
}
LogicNode.addLink(node, toNode, linkData.fromIndex, toIndex);
}
}
}
}
public static function patchUpdateNodeProp(treeName: String, nodeName: String, propName: String, value: Dynamic) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
Reflect.setField(node, propName, value);
}
}
public static function patchUpdateNodeInputVal(treeName: String, nodeName: String, socketIndex: Int, value: Dynamic) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.inputs[socketIndex].set(value);
}
}
public static function patchNodeDelete(treeName: String, nodeName: String) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.clearOutputs();
node.clearInputs();
tree.nodes.remove(nodeName);
}
}
public static function patchNodeCreate(treeName: String, nodeName: String, nodeType: String, propDatas: Array<Array<Dynamic>>, inputDatas: Array<Array<Dynamic>>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
// No further constructor parameters required here, all variable nodes
// use optional further parameters and all values are set later in this
// function.
var newNode: LogicNode = Type.createInstance(Type.resolveClass(nodeType), [tree]);
newNode.name = nodeName;
tree.nodes[nodeName] = newNode;
for (propData in propDatas) {
Reflect.setField(newNode, propData[0], propData[1]);
}
var i = 0;
for (inputData in inputDatas) {
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
}
i = 0;
for (outputData in outputDatas) {
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
}
}
}
public static function patchNodeCopy(treeName: String, nodeName: String, newNodeName: String, copyProps: Array<String>, inputDatas: Array<Array<Dynamic>>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
// No further constructor parameters required here, all variable nodes
// use optional further parameters and all values are set later in this
// function.
var newNode: LogicNode = Type.createInstance(Type.getClass(node), [tree]);
newNode.name = newNodeName;
tree.nodes[newNodeName] = newNode;
for (propName in copyProps) {
Reflect.setField(newNode, propName, Reflect.field(node, propName));
}
var i = 0;
for (inputData in inputDatas) {
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
}
i = 0;
for (outputData in outputDatas) {
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
}
}
}
#end #end
} }

View file

@ -2,14 +2,29 @@ package armory.trait.internal;
import kha.Image; import kha.Image;
import kha.Video; import kha.Video;
import iron.Trait; import iron.Trait;
import iron.object.MeshObject; import iron.object.MeshObject;
/**
Replaces the diffuse texture of the first material of the trait's object
with a video texture.
@see https://github.com/armory3d/armory_examples/tree/master/material_movie
**/
class MovieTexture extends Trait { class MovieTexture extends Trait {
/**
Caches all render targets used by this trait for re-use when having
multiple videos of the same size. The lookup only takes place on trait
initialization.
Map layout: `[width => [height => image]]`
**/
static var imageCache: Map<Int, Map<Int, Image>> = new Map();
var video: Video; var video: Video;
public static var image: Image; var image: Image;
public static var created = false;
var videoName: String; var videoName: String;
@ -33,10 +48,7 @@ class MovieTexture extends Trait {
this.videoName = videoName; this.videoName = videoName;
if (!created) { notifyOnInit(init);
created = true;
notifyOnInit(init);
}
} }
function init() { function init() {
@ -44,9 +56,21 @@ class MovieTexture extends Trait {
video = vid; video = vid;
video.play(true); video.play(true);
image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height())); var w = getPower2(video.width());
var h = getPower2(video.height());
var o = cast(object, iron.object.MeshObject); // Lazily fill the outer map
var hMap: Map<Int, Image> = imageCache[w];
if (hMap == null) {
imageCache[w] = new Map<Int, Image>();
}
image = imageCache[w][h];
if (image == null) {
imageCache[w][h] = image = Image.createRenderTarget(w, h);
}
var o = cast(object, MeshObject);
o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture
notifyOnRender2D(render); notifyOnRender2D(render);
}); });

View file

@ -0,0 +1,328 @@
package armory.trait.internal;
import iron.object.DecalObject;
import iron.object.MeshObject;
import iron.Trait;
import kha.Image;
import iron.math.Vec4;
import iron.data.MaterialData;
import iron.Scene;
import iron.object.Object;
import iron.object.Uniforms;
class UniformsManager extends Trait{
static var floatsRegistered = false;
static var floatsMap = new Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>();
static var vectorsRegistered = false;
static var vectorsMap = new Map<Object, Map<MaterialData, Map<String, Vec4>>>();
static var texturesRegistered = false;
static var texturesMap = new Map<Object, Map<MaterialData, Map<String, kha.Image>>>();
static var sceneRemoveInitalized = false;
public var uniformExists = false;
public function new(){
super();
notifyOnInit(init);
notifyOnRemove(removeObject);
if(! sceneRemoveInitalized){
Scene.active.notifyOnRemove(removeScene);
}
}
function init() {
if(Std.isOfType(object, MeshObject)){
var materials = cast(object, MeshObject).materials;
for (material in materials){
var exists = registerShaderUniforms(material);
if(exists) {
uniformExists = true;
}
}
}
#if rp_decals
if(Std.isOfType(object, DecalObject)){
var material = cast(object, DecalObject).material;
var exists = registerShaderUniforms(material);
if(exists) {
uniformExists = true;
}
}
#end
}
static function removeScene() {
removeObjectFromAllMaps(Scene.active.root);
}
function removeObject() {
removeObjectFromAllMaps(object);
}
// Helper method to register float, vec3 and texture getter functions
static function register(type: UniformType){
switch (type){
case Float:
if(! floatsRegistered){
floatsRegistered = true;
Uniforms.externalFloatLinks.push(floatLink);
}
case Vector:
if(! vectorsRegistered){
vectorsRegistered = true;
Uniforms.externalVec3Links.push(vec3Link);
}
case Texture:
if(! texturesRegistered){
texturesRegistered = true;
Uniforms.externalTextureLinks.push(textureLink);
}
}
}
// Register and map shader uniforms if it is an armory shader parameter
public static function registerShaderUniforms(material: MaterialData) : Bool {
var uniformExist = false;
if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null);
if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null);
if(! texturesMap.exists(Scene.active.root)) texturesMap.set(Scene.active.root, null);
for(context in material.shader.raw.contexts){ // For each context in shader
for (constant in context.constants){ // For each constant in the context
if(constant.is_arm_parameter){ // Check if armory parameter
uniformExist = true;
var object = Scene.active.root; // Map default uniforms to scene root
switch (constant.type){
case "float":{
var link = constant.link;
var value = constant.float;
setFloatValue(material, object, link, value);
register(Float);
}
case "vec3":{
var vec = new Vec4();
vec.x = constant.vec3.get(0);
vec.y = constant.vec3.get(1);
vec.z = constant.vec3.get(2);
setVec3Value(material, object, constant.link, vec);
register(Vector);
}
}
}
}
for (texture in context.texture_units){
if(texture.is_arm_parameter){ // Check if armory parameter
uniformExist = true;
var object = Scene.active.root; // Map default texture to scene root
if(texture.default_image_file == null){
setTextureValue(material, object, texture.link, null);
}
else{
iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) {
setTextureValue(material, object, texture.link, image);
});
}
register(Texture);
}
}
}
return uniformExist;
}
// Method to set map Object -> Material -> Link -> FLoat
public static function setFloatValue(material: MaterialData, object: Object, link: String, value: Null<kha.FastFloat>){
if(object == null || material == null || link == null) return;
var map = floatsMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Method to set map Object -> Material -> Link -> Vec3
public static function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){
if(object == null || material == null || link == null) return;
var map = vectorsMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Method to set map Object -> Material -> Link -> Texture
public static function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){
if(object == null || material == null || link == null) return;
var map = texturesMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Mehtod to get object specific material parameter float value
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
if(object == null || mat == null) return null;
if(! floatsMap.exists(object)){
object = Scene.active.root;
}
var material = floatsMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Mehtod to get object specific material parameter vec3 value
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
if(object == null || mat == null) return null;
if(! vectorsMap.exists(object)){
object = Scene.active.root;
}
var material = vectorsMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Mehtod to get object specific material parameter texture value
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
if(object == null || mat == null) return null;
if(! texturesMap.exists(object)){
object = Scene.active.root;
}
var material = texturesMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Returns complete map of float value material paramets
public static function getFloatsMap():Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>{
return floatsMap;
}
// Returns complete map of vec3 value material paramets
public static function getVectorsMap():Map<Object, Map<MaterialData, Map<String, Vec4>>>{
return vectorsMap;
}
// Returns complete map of texture value material paramets
public static function getTexturesMap():Map<Object, Map<MaterialData, Map<String, kha.Image>>>{
return texturesMap;
}
// Remove all object specific material paramenter keys
public static function removeObjectFromAllMaps(object: Object) {
floatsMap.remove(object);
vectorsMap.remove(object);
texturesMap.remove(object);
}
// Remove object specific material paramenter keys
public static function removeObjectFromMap(object: Object, type: UniformType) {
switch (type){
case Float: floatsMap.remove(object);
case Vector: vectorsMap.remove(object);
case Texture: texturesMap.remove(object);
}
}
}
@:enum abstract UniformType(Int) from Int to Int {
var Float = 0;
var Vector = 1;
var Texture = 2;
}

View file

@ -18,8 +18,9 @@ class PhysicsConstraintExportHelper extends iron.Trait {
var breakingThreshold: Float; var breakingThreshold: Float;
var limits: Array<Float>; var limits: Array<Float>;
var constraintAdded: Bool = false; var constraintAdded: Bool = false;
var relativeConstraint: Bool = false;
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) { public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, relatieConstraint: Bool = false, limits: Array<Float> = null) {
super(); super();
this.body1 = body1; this.body1 = body1;
@ -27,14 +28,26 @@ class PhysicsConstraintExportHelper extends iron.Trait {
this.type = type; this.type = type;
this.disableCollisions = disableCollisions; this.disableCollisions = disableCollisions;
this.breakingThreshold = breakingThreshold; this.breakingThreshold = breakingThreshold;
this.relativeConstraint = relatieConstraint;
this.limits = limits; this.limits = limits;
notifyOnInit(init); notifyOnInit(init);
notifyOnUpdate(update); notifyOnUpdate(update);
} }
function init() { function init() {
var target1 = Scene.active.getChild(body1); var target1;
var target2 = Scene.active.getChild(body2); var target2;
if(relativeConstraint) {
target1 = object.parent.getChild(body1);
target2 = object.parent.getChild(body2);
}
else {
target1 = Scene.active.getChild(body1);
target2 = Scene.active.getChild(body2);
}
object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits)); object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits));
constraintAdded = true; constraintAdded = true;
} }

View file

@ -207,7 +207,7 @@ class PhysicsWorld extends Trait {
/** /**
Used to get intersecting rigid bodies with the passed in RigidBody as reference. Often used when checking for object collisions. Used to get intersecting rigid bodies with the passed in RigidBody as reference. Often used when checking for object collisions.
@param body The passed in RigidBody to be checked for intersecting rigid bodies. @param body The passed in RigidBody to be checked for intersecting rigid bodies.
@return Array<RigidBody> or null. @return `Array<RigidBody>`
**/ **/
public function getContacts(body: RigidBody): Array<RigidBody> { public function getContacts(body: RigidBody): Array<RigidBody> {
if (contacts.length == 0) return null; if (contacts.length == 0) return null;

View file

@ -149,7 +149,7 @@ class RigidBody extends iron.Trait {
if (ready) return; if (ready) return;
ready = true; ready = true;
if (!Std.is(object, MeshObject)) return; // No mesh data if (!Std.isOfType(object, MeshObject)) return; // No mesh data
transform = object.transform; transform = object.transform;
physics = armory.trait.physics.PhysicsWorld.active; physics = armory.trait.physics.PhysicsWorld.active;

Some files were not shown because too many files have changed in this diff Show more